# Typeroll CMS Source: [Typeroll CMS](https://typeroll.com/docs/) > Documentation for editing content, connecting your AI agent and publishing static websites. Typeroll CMS is an open-source CMS with a visual editor, a REST API and an MCP server. Use the browser editor for content changes, or connect your own AI agent to build and manage sites. You can start editing and reviewing previews before connecting publishing accounts. ## Choose a guide [Section titled “Choose a guide”](https://typeroll.com/docs/#choose-a-guide) [Visual editor](https://typeroll.com/docs/guides/the-editor/)Add blocks, edit text and images, review drafts and work on mobile. [AI agents and MCP](https://typeroll.com/docs/getting-started/mcp-server/)Connect an MCP-compatible agent using the hosted endpoint or a local MCP process. [Publishing and Cloudflare](https://typeroll.com/docs/publishing/cloudflare/)Set up GitHub source, shared cloud builds, static Pages hosting and R2 media. [Website and media domains](https://typeroll.com/docs/publishing/domains/)Configure Hosting Groups, shared media and site domains. Prepare a domain change before switching traffic. [Migrate an existing website](https://typeroll.com/docs/guides/wordpress-migration/)Start with WordPress, or follow the separate Squarespace, Wix and Webflow guides in Migration. [Self-host Typeroll CMS](https://typeroll.com/docs/guides/self-hosting/)Run the CMS, API, Forms and Extension runtime on your own infrastructure. ## See the editor [Section titled “See the editor”](https://typeroll.com/docs/#see-the-editor) ![Typeroll CMS editor showing the block structure, live page preview and heading fields for a sample website.](https://typeroll.com/docs/images/editor-desktop.png) [Open the editor guide](https://typeroll.com/docs/guides/the-editor/) for the desktop and mobile workflows. ## Software, hosting and connected services [Section titled “Software, hosting and connected services”](https://typeroll.com/docs/#software-hosting-and-connected-services) The open-source edition is free to self-host. Infrastructure and connected services have their own costs. [Typeroll Cloud](https://typeroll.com/) is the managed service; these docs describe the CMS and its supported workflows. Published sites are static files. Forms, Apps and Extensions can call separate runtime services. See [Cloudflare hosting and builds](https://typeroll.com/docs/publishing/cloudflare/) for the boundary between source code, build execution and hosting. ## Documentation for your agent [Section titled “Documentation for your agent”](https://typeroll.com/docs/#documentation-for-your-agent) Use [llms.txt](https://typeroll.com/docs/llms.txt) as the documentation index, or [llms-full.txt](https://typeroll.com/docs/llms-full.txt) for the complete text. The index also links to a separate text version of every page, so your agent can fetch only what it needs. Text exports contain absolute links and source addresses and do not require JavaScript or a login. These files describe the product; they do not grant access to your sites. API and MCP requests still need your credentials and the appropriate permissions. --- # Analytics Source: [Analytics](https://typeroll.com/docs/apps/analytics/) > Traffic measurement, campaign attribution, first-party conversion events, Core Web Vitals, and AI-assistant referral insights. * **Type:** Core module * **Build impact:** Adds small, independently loaded browser runtimes * **Modules:** Traffic, Attribution, Events and conversions Analytics is the product surface for understanding traffic and outcomes. Its modules remain technically independent: campaign forwarding must still work if traffic reporting or an external analytics provider is unavailable. | Module | Purpose | Reference | | ---------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | | Traffic | Cookieless visits, page views, referrers, Core Web Vitals, and AI-assistant referrals | This page | | Attribution | Preserve approved campaign context and forward it to exact destinations | [Attribution](https://typeroll.com/docs/apps/funnel-attribution/) | | Events and conversions | Record validated first-party funnel events and report their sources | [Events and conversions](https://typeroll.com/docs/apps/events/) | ## Configuration [Section titled “Configuration”](https://typeroll.com/docs/apps/analytics/#configuration) | Field | Purpose | Exposure | | -------------- | ----------------------------------------------------- | ------------------------------ | | `beacon_token` | Identifies the site to the browser beacon | Included in the published HTML | | `site_tag` | Identifies the property when the portal reads reports | Server-side only | Hosted installations can provision these identifiers automatically when the site has a usable domain and the platform has Cloudflare access. Self-hosted installations can supply them from Cloudflare. First-party conversion events also require a public portal or forms-service URL and the platform signing secret; without both, builds omit the event endpoint while traffic and attribution continue to work. ## Traffic insights [Section titled “Traffic insights”](https://typeroll.com/docs/apps/analytics/#traffic-insights) The Traffic module reports visits and page views over 7, 30, or 90 days, top referrers, Core Web Vitals, and visits referred by AI assistants. AI referrals are ordinary referral traffic at the collection layer; Insights classifies their referring hosts so each assistant can be reported separately. This complements the `llms.txt`, semantic HTML, and structured data Typeroll publishes to help search engines and assistants understand the site. See [SEO and indexing](https://typeroll.com/docs/technical/seo-and-indexing/). ## Runtime and privacy [Section titled “Runtime and privacy”](https://typeroll.com/docs/apps/analytics/#runtime-and-privacy) The beacon is emitted during the static build. Cloudflare Web Analytics is cookieless and intended for aggregate traffic measurement. Confirm the legal requirements that apply to your site and jurisdiction before relying on a particular consent treatment. Attribution storage and conversion events follow the site’s consent decision. The Traffic module does not automatically gain access to attribution cookies, and saved campaign values are not sent to third parties merely because they exist. See each module’s privacy contract before combining them. ## Insights states [Section titled “Insights states”](https://typeroll.com/docs/apps/analytics/#insights-states) The Insights surface distinguishes a disabled module, missing server configuration, a missing site tag, and a valid property with no data yet. This lets operators diagnose setup without treating every empty report as the same problem. | State | Meaning | | ---------------- | ----------------------------------------------------------------- | | `app_disabled` | Analytics is not active for the site | | `not_configured` | The installation has no Cloudflare analytics credentials | | `no_site_tag` | Analytics is active but not linked to a Cloudflare property | | `no_data` | The property is linked but has no traffic for the selected period | | `ok` | Traffic totals and breakdowns are available | The same surface reports first-party conversion events separately. Conversion reporting can continue to show data when Cloudflare traffic reporting is temporarily unavailable. --- # Directory Source: [Directory](https://typeroll.com/docs/apps/directory/) > Passwordless, self-service editing for businesses and other entities listed in a Typeroll collection. * **Type:** Core module * **Build impact:** Contributes forms and republishes edited listing content * **Authentication:** Single-use, time-limited links Directory lets an entity listed in a collection request a private edit link and correct its own data without creating an account. The link is sent only to the email address already stored on that listing. ## Configuration [Section titled “Configuration”](https://typeroll.com/docs/apps/directory/#configuration) | Field | Purpose | | ------------------- | ------------------------------------------------------ | | Listings collection | Collection containing the directory records | | Contact email field | Existing field that may receive an edit link | | Link lifetime | Validity period for issued links, capped at seven days | The module seeds two ordinary Typeroll forms: one to request a link and one to edit the listing. Sites may extend those forms using the normal form editor. ## Write boundaries [Section titled “Write boundaries”](https://typeroll.com/docs/apps/directory/#write-boundaries) An edit session is bound to one listing. It can update only collection fields whose write policy permits owner writes. Requests for an unrelated email address do not reveal whether a listing exists, and enabling the module is what opens its public endpoints. Directory builds on general platform primitives: collection field authority, prefill sources, form actions, passwordless sessions, and coalesced deploys. Only the listing-specific workflow belongs to the module. --- # Events and conversions Source: [Events and conversions](https://typeroll.com/docs/apps/events/) > Consent-gated first-party conversion events from validated Analytics attribution funnels. Events and conversions is the first-party outcome layer of Analytics. A funnel target can declare a `click_event`; when the visitor activates that target, Typeroll records the event without delaying navigation. ## Event contract [Section titled “Event contract”](https://typeroll.com/docs/apps/events/#event-contract) An accepted event contains only: * the declared event name; * funnel id and destination label; * the current page path; * resolved attribution fields already allowlisted by that funnel; * server-generated creation and expiry timestamps. The endpoint does not store an IP address, cookie id, user id, link text, full destination URL, referrer, or arbitrary browser properties. Undeclared event names and attribution fields are rejected or discarded. ## Authenticity and abuse protection [Section titled “Authenticity and abuse protection”](https://typeroll.com/docs/apps/events/#authenticity-and-abuse-protection) Published builds receive a site-bound signed token. The intake verifies the signature, request origin, enabled Analytics and Attribution modules, active funnel id, event name, destination, page path, and parameter allowlist. It also applies per-IP and per-site rate limits. The browser uses `sendBeacon` with a non-blocking keepalive fallback. The public token proves that an event came from a published site build; it is not a secret visitor credential. Server-side validation remains authoritative. ## Consent and delivery [Section titled “Consent and delivery”](https://typeroll.com/docs/apps/events/#consent-and-delivery) When the site uses Typeroll’s consent banner, no first-party conversion event is sent before optional consent. With no banner, the site’s configured consent policy applies. Attribution cookies remain a separate mechanism and are never required for a current-page event. If Google Analytics is installed, the same declared click can also be sent to `gtag`. Failure in Typeroll Analytics or an external adapter never blocks the visitor’s navigation. ## Storage and reporting [Section titled “Storage and reporting”](https://typeroll.com/docs/apps/events/#storage-and-reporting) Events expire after 90 days; expired rows are removed during event intake and reporting. Insights reports total conversion events, counts by event and destination, and top `utm_source` and `utm_campaign` values for 7, 30, or 90 days. A report is marked truncated if more than 5,000 matching raw events are returned for one period. This is event attribution, not person-level tracking. Do not use campaign parameter names to smuggle email addresses, phone numbers, or other personal data into the event pipeline. --- # Forms Source: [Forms](https://typeroll.com/docs/apps/forms/) > Server-backed contact forms and multi-step funnels with validation, partial saves, submissions, and notifications. Forms is Typeroll’s built-in core module for collecting structured information from a published site. It covers simple contact forms, booking requests, applications, lead funnels, and other multi-step flows without requiring a third-party form provider. ## Availability [Section titled “Availability”](https://typeroll.com/docs/apps/forms/#availability) Forms is included in every Typeroll Cloud site and every self-hosted Typeroll installation. It requires no premium Typeroll App, Extension, or separate subscription. Typeroll Cloud sends submissions to the hosted Forms service; a self-hosted installation sends them to its own Forms endpoint and datastore. The published customer site remains static in both cases. Cloud plans may set submission-volume, retention, or delivery limits. Those limits do not turn Forms into an optional module. Self-hosted operators control their own capacity, storage, retention, and delivery costs. ## Capabilities [Section titled “Capabilities”](https://typeroll.com/docs/apps/forms/#capabilities) * single-step and multi-step forms; * reusable field blocks with client-side and server-side validation; * partial saves as a visitor advances through a funnel; * a submissions inbox and agent-readable submissions; * email notifications through a configured email provider; * post-submit actions, success messages, and redirects; * module-contributed forms, actions, and prefill sources. Forms are stored as steps. Each step contains the same block tree used by the rest of Typeroll, so a simple form is one step and a funnel is several steps. The `core/form` block places a form on a page and wires its signed submission token and runtime automatically. ## Runtime and protection [Section titled “Runtime and protection”](https://typeroll.com/docs/apps/forms/#runtime-and-protection) Published forms submit to Typeroll’s forms service. Each submission is checked with a signed token, a honeypot, server-side validation, and rate limiting. Multi-step forms can save partial answers between steps. Submission data is not embedded in the static site build. Do not collect sensitive or regulated data unless the site’s legal basis, retention, access controls, and downstream delivery have been reviewed for that specific use case. ## Delivery [Section titled “Delivery”](https://typeroll.com/docs/apps/forms/#delivery) Accepted submissions are stored for the site’s submissions inbox. A form can also run configured actions, such as sending an email notification. Email credentials are configured separately under the site’s email settings, stored encrypted, and kept off the agent-readable surface. ## Agent and API surface [Section titled “Agent and API surface”](https://typeroll.com/docs/apps/forms/#agent-and-api-surface) Agents can create, read, update, list, and delete forms. They can also list and delete submissions. A caller may provide a flat field list for a simple form; Typeroll converts it to a one-step definition. Multi-step flows use explicit steps. See [Forms tools](https://typeroll.com/docs/tools/forms/) for the API-oriented reference and [Build forms](https://typeroll.com/docs/skills/tr-forms/) for the task workflow. ## Scope boundary [Section titled “Scope boundary”](https://typeroll.com/docs/apps/forms/#scope-boundary) The Forms module documents the general form engine. A narrower flow such as a quote request, event registration, lead qualification funnel, or directory edit form should be documented as a recipe or as part of the Extension that ships that form. Customer-specific questions, recipients, legal copy, and routing stay in the site’s configuration rather than this module reference. --- # Analytics attribution Source: [Analytics attribution](https://typeroll.com/docs/apps/funnel-attribution/) > Forward allowlisted campaign parameters, persist consented first- and last-touch attribution, and emit destination click events. * **Type:** Analytics module * **Build impact:** Adds a small data-driven browser runtime * **Targets:** Exact HTTPS host and path rules The Attribution module carries approved query parameters from a landing page to a link destination. It can also persist real incoming values as first-touch or last-touch snapshots and reuse them on a later visit. ## Resolution order [Section titled “Resolution order”](https://typeroll.com/docs/apps/funnel-attribution/#resolution-order) For every allowlisted parameter, the runtime resolves values in this order: 1. a valid value on the current page URL; 2. the configured first-touch or last-touch snapshot; 3. an explicitly configured synthetic fallback; 4. no outgoing parameter. Fallbacks are synthetic attribution A fallback does not preserve an advertising value. It invents a value when no incoming or stored value exists. The API rejects fallback configuration unless `allow_synthetic_fallbacks=true` explicitly acknowledges that effect. ## Storage and consent [Section titled “Storage and consent”](https://typeroll.com/docs/apps/funnel-attribution/#storage-and-consent) Storage is independent from direct same-page forwarding. When enabled, the module stores compact versioned snapshots in `tr_attr_first_v1` and/or `tr_attr_last_v1`. Cookies use `Secure`, `SameSite=Lax`, and `Path=/`; the default lifetime is 30 days. They are host-only unless a cookie domain is explicitly configured. No attribution cookie is written before optional consent. Direct forwarding from the current URL can still work without storage. Rejecting or withdrawing consent removes the module’s attribution cookies. ## Safety rules [Section titled “Safety rules”](https://typeroll.com/docs/apps/funnel-attribution/#safety-rules) * Only declared parameter names are read and forwarded. * Personal-data-like names are rejected unless separately allowed. * Values have configurable length limits and reject control characters. * Targets match protocol, hostname, and pathname structurally. * Existing unrelated target parameters and URL fragments are preserved. * Initialization is idempotent and never waits for analytics before navigation. ## Agent and API support [Section titled “Agent and API support”](https://typeroll.com/docs/apps/funnel-attribution/#agent-and-api-support) Agents can use `read_funnel_attribution` and `update_funnel_attribution` with admin permission. They should read the current state first and deploy after a successful update. The same validation applies to the bearer API and portal configuration. See [Booking-link attribution](https://typeroll.com/docs/recipes/booking-link-attribution/) for a neutral implementation pattern. ## Relationship to Analytics events [Section titled “Relationship to Analytics events”](https://typeroll.com/docs/apps/funnel-attribution/#relationship-to-analytics-events) Attribution does not depend on traffic measurement. Link rewriting and cookie resolution still work when no Analytics beacon or external provider is available. When both Analytics and a target’s `click_event` are configured, the runtime also sends a non-blocking first-party event. It contains the event name, funnel, destination, page path, and only the resolved allowlisted attribution fields. The signed event endpoint verifies the active funnel rule before storage. See [Events and conversions](https://typeroll.com/docs/apps/events/) for retention and reporting. --- # Integrations Source: [Integrations](https://typeroll.com/docs/apps/integrations/) > Maintained, validated site-wide tags for analytics, advertising, marketing, and support providers. * **Type:** Integration module * **Build impact:** Injects configured provider tags * **Consent:** Uses Typeroll’s existing consent categories Integrations replaces pasted third-party snippets with maintained platform code around validated provider identifiers. A malformed identifier is dropped instead of being embedded into the site. When a provider changes its standard tag, Typeroll can update the shared implementation centrally. ## Supported providers [Section titled “Supported providers”](https://typeroll.com/docs/apps/integrations/#supported-providers) | Group | Providers | | ----------- | --------------------------------------------------------------------------------------------------------------------------- | | Analytics | Google Analytics 4, Google Tag Manager, Hotjar, Microsoft Clarity, Plausible, Fathom Analytics, Matomo | | Advertising | Meta Pixel, LinkedIn Insight Tag, TikTok Pixel, Pinterest Tag, Snap Pixel, Reddit Pixel, X Pixel, Microsoft Advertising UET | | Marketing | HubSpot, Mailchimp Connected Site | | Support | Intercom, Crisp Chat, Tawk.to | Each provider declares its identifier fields, validation pattern, placement, and consent category. Client identifiers are public by design because the provider’s browser tag needs them; they are not treated as server secrets. ## Consent behavior [Section titled “Consent behavior”](https://typeroll.com/docs/apps/integrations/#consent-behavior) Providers use the shared categories `necessary`, `functional`, `analytics`, and `marketing`. If the site uses Typeroll’s consent banner, non-necessary tags remain inert until the matching optional consent is granted. Google Tag Manager uses the marketing category because a container may load advertising tags. ## Scope [Section titled “Scope”](https://typeroll.com/docs/apps/integrations/#scope) This module owns site-wide tags placed in the document head or at the end of the body. A widget or embed placed at a particular point in page content belongs in a block, not in this catalog. --- # Core modules overview Source: [Core modules overview](https://typeroll.com/docs/apps/overview/) > Built-in Typeroll capabilities, how core modules differ from premium Typeroll Apps, Extensions, and reusable recipes. Typeroll core modules are CMS capabilities with their own configuration, runtime behavior, validation, and lifecycle. A module can add code to a published site, expose server-side endpoints, contribute forms or blocks, or unlock an admin feature. Some modules are always available; others are enabled per site. “Typeroll Apps” is a different term: it names the separately sold premium app collection operated exclusively in Typeroll-controlled accounts. It is not a synonym for the open-source core module registry. A self-hosted CMS contains the Extension support needed to buy and use Typeroll Apps, but never hosts the premium app backend. ## Modules, Apps, Extensions, and recipes [Section titled “Modules, Apps, Extensions, and recipes”](https://typeroll.com/docs/apps/overview/#modules-apps-extensions-and-recipes) | Label | Meaning | Public documentation | | ---------------------- | ---------------------------------------------------------- | ----------------------------------------------------- | | **Core module** | A general capability implemented in the CMS core | One stable module reference | | **Typeroll App** | A premium app operated in Typeroll’s own accounts | Product documentation and subscription terms | | **Extension** | A SaaS or bespoke app operated in its developer’s accounts | Extension documentation | | **Integration module** | A core adapter for external services | One reference plus its supported provider catalog | | **Recipe** | A reusable configuration of one or more modules | A task-focused guide with no customer-specific values | | **Bespoke Extension** | Behavior built for one customer’s domain or workflow | Documented privately unless it becomes reusable | The distinction is about product scope, not who may enable a capability. Public module docs describe what exists and how it behaves. Account availability and rollout policy can change independently. ## Available core modules [Section titled “Available core modules”](https://typeroll.com/docs/apps/overview/#available-core-modules) | Module | Type | What it provides | Runtime | | ------------------------------------------------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | [Analytics](https://typeroll.com/docs/apps/analytics/) | Core module | Traffic, Core Web Vitals, AI referrals, campaign attribution, first-party events, and conversion reporting | Browser runtimes, event intake, and portal reports | | [Forms](https://typeroll.com/docs/apps/forms/) | Core module | Contact forms, multi-step funnels, partial saves, submissions, and notifications | Published-site runtime and forms service | | [Integrations](https://typeroll.com/docs/apps/integrations/) | Integration module | Maintained tags for analytics, advertising, marketing, and support providers | Build-time tag injection with consent gating | | [Directory](https://typeroll.com/docs/apps/directory/) | Core module | Passwordless self-service editing for directory listings | Server endpoints, forms, and collection writes | ## Lifecycle [Section titled “Lifecycle”](https://typeroll.com/docs/apps/overview/#lifecycle) Optional core modules use a code-defined registry. Each definition declares its configuration fields, validation, public build data, and any forms, blocks, actions, or endpoints it contributes. Always-on core modules can expose the same primitives without requiring a per-site activation switch. Modules that affect a static build require a deploy before the new runtime reaches visitors. ## Manage modules with the publishing API key [Section titled “Manage modules with the publishing API key”](https://typeroll.com/docs/apps/overview/#manage-modules-with-the-publishing-api-key) An admin-scoped Typeroll API key can manage the same legacy `apps` registry as the portal. This is the recommended surface for agents and automated publishing systems: ```text GET /api/v1/sites/{siteId}/apps GET /api/v1/sites/{siteId}/apps/{appId} PUT /api/v1/sites/{siteId}/apps/{appId} ``` The list and read responses include each module’s field schema and masked state. The write body is `{ "enabled": true, "config": { ... } }`. Omitted fields preserve their current values. Secret fields are encrypted server-side and are never returned; audit entries record only the names of submitted config fields, not their values. Analytics provisioning and module-provided forms or blocks run through the same service whether the change comes from the portal or API. MCP clients expose this contract as `list_apps`, `read_app`, and `update_app`. Use `trigger_deploy` after a successful update when `affects_build` is true. Module management requires admin permission; a read- or write-only site share cannot change activation or configuration. The public reference for each module covers: * capability and intended scope; * configuration and validation; * visitor data, cookies, and consent; * build or server runtime behavior; * agent and API surfaces; * operational verification. Narrow solutions should link back to these references instead of duplicating the platform contract. --- # Provider backend & admin SSO Source: [Provider backend & admin SSO](https://typeroll.com/docs/extensions/backend/) > Verify direct Extension tokens, delegated admin identity, self-hosted issuers, and lifecycle webhooks. The provider backend owns business data and authorization. Typeroll supplies two distinct identity types; they must not be interchangeable: * a short-lived delegated user token for an admin launch; * a public Extension token attached to a direct frontend API request. Always validate the token issuer dynamically. A hosted portal and a self-hosted portal publish discovery and JWKS from different domains. ## Admin launch [Section titled “Admin launch”](https://typeroll.com/docs/extensions/backend/#admin-launch) Typeroll posts a 60-second launch code, issuer, installation ID, and page ID to the manifest’s exact `launch_url`. Exchange the code server-to-server: ```http POST {issuer}/api/extensions/token Content-Type: application/json { "grant_type": "authorization_code", "code": "…", "client_id": "…", "client_secret": "…" } ``` Validate the returned ES256 JWT against the issuer’s discovery document and JWKS. Check at least `iss`, `aud`, `sub`, `org_id`, `site_id`, `installation_id`, `permission`, `scopes`, `jti`, `iat`, and `exp`. The client secret and access token must never reach frontend code or logs. ## Direct frontend requests [Section titled “Direct frontend requests”](https://typeroll.com/docs/extensions/backend/#direct-frontend-requests) Frontend code calls a declared relative path with `context.api.fetch()`. The browser sends the request directly to `api.base_url`; Typeroll does not receive or forward its payload. Configure CORS for each installed site origin and accept the `X-Typeroll-Extension-Token` request header. When `api.authentication` is `signed_installation`, verify that token’s ES256 signature and `iss`, `aud`, `sub`, `token_use=public_extension`, `org_id`, `site_id`, `installation_id`, `origin`, `jti`, `iat`, and `exp`. Fetch keys from the issuer’s discovery/JWKS endpoints. The token proves installation identity only. Authenticate and authorize the visitor, recipient token and business action separately. For bundled components, require the verified `origin` claim to equal the request’s browser `Origin` header. Embedded apps use Typeroll’s validated browser-side message bridge, so the provider request is likewise made by the published site origin. Never use the token claim to relax CORS dynamically before the signature and issuer have been verified. Editor previews are the exception to ordinary site-origin binding: their sandbox has an opaque origin, so both the browser `Origin` header and the signed preview token’s `origin` claim are the literal string `null`. That equality does not identify a site. Require the token’s `preview: true` claim and enforce its exact `preview_routes` method/path allowlist server-side. Do not accept a site or organization ID supplied by browser input when a verified Extension token already contains that identity. Do not put durable provider credentials in frontend code. Typeroll’s route declaration is a client contract, not a provider-side access-control substitute. There is no supported architecture where Typeroll or a generated customer Function reverse-proxies this request. Third-party and bespoke backends remain in the app developer’s accounts. Typeroll Apps use the same direct contract, but their backends are operated separately in Typeroll-controlled accounts. ## Pair a self-hosted issuer [Section titled “Pair a self-hosted issuer”](https://typeroll.com/docs/extensions/backend/#pair-a-self-hosted-issuer) If the manifest declares `auth.pairing_url`, an administrator can explicitly pair a portal. The provider receives an issuer, discovery URL, nonce, JWKS fingerprint, and signed pairing assertion. Fetch discovery and JWKS from the declared issuer, compare the canonical JWKS fingerprint, verify the assertion, then store the trusted issuer. Never trust keys included only in the inbound request body. ## Lifecycle webhooks [Section titled “Lifecycle webhooks”](https://typeroll.com/docs/extensions/backend/#lifecycle-webhooks) Typeroll signs the exact raw body with HMAC-SHA256: ```text X-Typeroll-Event X-Typeroll-Event-Id X-Typeroll-Timestamp X-Typeroll-Signature: v1= ``` Verify the signature over `.`, reject stale timestamps, and deduplicate `X-Typeroll-Event-Id`. Return a success response for an event already processed. Delivery retries network failures, 408, 429, and 5xx. ## Service credentials [Section titled “Service credentials”](https://typeroll.com/docs/extensions/backend/#service-credentials) Server-to-server calls into Typeroll use an installation credential, not an admin launch token. Send it as Bearer along with the owner organization and installation headers required by the site API. Store the secret outside source control and rotate it from the Extension installation settings. See the [manifest reference](https://typeroll.com/docs/extensions/manifest/) for the declared surfaces. --- # Frontend & recipient links Source: [Frontend & recipient links](https://typeroll.com/docs/extensions/frontend/) > Use the Extension runtime, direct provider APIs, internal navigation, and opaque recipient URL context safely. A bundled component is an ES module with one exported function: ```ts export async function mount(element, props, context) { // Render into element. Return after initial setup is complete. } ``` Typeroll calls `mount` once per block instance. The runtime context is scoped to that mount and contains: * protocol, runtime, Extension, installation, and component identifiers; * `preview`, which is `true` only inside an isolated editor preview; * the installation’s public configuration; * `url.get()`, `url.has()`, and one-time `url.consume()` accessors; * an in-memory `navigation` object; * `site.url()` and `site.navigate()` for root-relative Typeroll page paths; * installation-scoped `storage.session` and `storage.local` JSON storage; * `api.fetch()` for direct calls to declared provider routes; * `forms.has()`, `forms.list()`, and `forms.submit()` for approved form bindings. ## Build a native lead tool on Typeroll Forms [Section titled “Build a native lead tool on Typeroll Forms”](https://typeroll.com/docs/extensions/frontend/#build-a-native-lead-tool-on-typeroll-forms) Create the receiving form in the site’s ordinary Forms module, then declare its ID in the component manifest and request `forms:submit`. The bespoke frontend can calculate, branch, animate, or collect data however it wants and submit only the final fields: ```ts const result = await context.forms.submit("lead", { name: "Ada Lovelace", email: "ada@example.com", team_size: 12, estimated_monthly_price: 4800, }); if (result.ok === false) { // Map result.errors to the custom interface. } else if (result.done) { // Show the custom confirmation state. } ``` Typeroll adds its signed token, honeypot protocol fields, and proof of work. The request goes directly to the absolute Forms endpoint embedded by the CMS. Typeroll Cloud sites use the hosted Forms service and self-hosted sites use their own Forms endpoint. No Function or proxy is installed in the customer’s static hosting project. Submissions, notifications, and configured webhooks behave exactly like submissions from a standard `core/form` block. The editor preview exposes binding names but intentionally rejects real submissions. Test writes on a deployed test page, where they appear in the normal Forms submission inbox. ## Navigate without changing the page path [Section titled “Navigate without changing the page path”](https://typeroll.com/docs/extensions/frontend/#navigate-without-changing-the-page-path) An Extension can implement several screens inside one block: ```ts context.navigation.subscribe((view) => render(view)); context.navigation.navigate("confirmation"); ``` Navigation belongs to the mounted component. It does not create a new Typeroll page or URL path, and the component’s captured runtime context remains available until that mount is destroyed. A full reload starts a new mount and follows the provider’s declared session strategy. ## Navigate between Typeroll pages [Section titled “Navigate between Typeroll pages”](https://typeroll.com/docs/extensions/frontend/#navigate-between-typeroll-pages) Use the site-navigation API instead of assigning a root-relative path directly: ```ts context.storage.session.set("quote-draft", { from: "Storgatan 1", to: "Kungsgatan 2", }); context.site.navigate("/flyttfirmeoffert/"); ``` On a published site, `site.url(path)` resolves against the site’s current origin. In a navigable preview it resolves to the matching preview route and preserves any signed preview ticket. Only root-relative paths are accepted. Both storage areas accept JSON-compatible values through `get`, `set`, and `remove`. They are namespaced by installation. On a published site they use the browser’s corresponding Web Storage area. Because an isolated preview has an opaque origin, preview storage is tab-scoped and survives navigation inside that preview; preview `local` intentionally has the same lifetime as preview `session`. Values are not added to the URL, referrer, generated HTML, or Typeroll request body. Preview changes the page that initiates browser requests: bundled code runs from the portal preview rather than the published site’s domain. A third-party browser key restricted by HTTP referrer or page origin must allow the portal’s preview origin to work there. Prefer a separate, narrowly restricted preview credential; do not loosen the production key just to make preview requests pass. ## Receive an opaque recipient token [Section titled “Receive an opaque recipient token”](https://typeroll.com/docs/extensions/frontend/#receive-an-opaque-recipient-token) Declare only the URL inputs that the component needs: ```json { "url_context": { "query": [ { "name": "quote", "expose_as": "quote_token", "sensitive": true, "consume": true, "max_length": 256, "pattern": "^[A-Za-z0-9_-]+$" } ] } } ``` The provider can email several recipients links to the same static Typeroll page, for example `/offer/?quote=opaque-value`. At mount time, Typeroll: 1. reads only declared query, fragment, path, or raw-query inputs; 2. rejects values that exceed the declared length or pattern; 3. captures values in the component’s private runtime closure; 4. removes inputs marked `consume` with `history.replaceState`; and 5. exposes the captured value through `context.url`. The token is not initial block state and is never inserted into generated HTML, saved page data, Typeroll’s datastore, or diagnostics. ```ts const quoteToken = context.url.consume("quote_token"); await context.api.fetch("/quotes/approve", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token: quoteToken }), }); ``` The provider remains responsible for generating a cryptographically random token and enforcing expiry, revocation, intended recipient, allowed actions, and replay behavior. A difficult-to-guess URL is a bearer credential, not an authorization model by itself. ## Call the provider directly [Section titled “Call the provider directly”](https://typeroll.com/docs/extensions/frontend/#call-the-provider-directly) Manifest v3 declares `api.base_url`, routes and an authentication mode. `context.api.fetch()` resolves only declared relative paths, sends the browser request directly to that provider URL with `credentials: omit`, and does not pass through Typeroll or Cloudflare Pages Functions. With `authentication: "signed_installation"`, the runtime first obtains a five-minute JWT from the CMS issuer and adds it as `X-Typeroll-Extension-Token`. The token confirms the enabled installation and requesting site origin. It is not a visitor login and does not authorize a quote, booking or wedding plan; the provider must still validate its own recipient token and requested action. The provider must allow the installed site origin in CORS and accept `X-Typeroll-Extension-Token` in its preflight policy. ## Embedded applications [Section titled “Embedded applications”](https://typeroll.com/docs/extensions/frontend/#embedded-applications) An `embedded_app` runs in a sandboxed provider-origin iframe. It receives a versioned `typeroll.extension.init` message and can request resize or internal navigation with the matching protocol messages. Validate message origin, source window, protocol version, installation ID, and component ID on every message. For the first init message, require `event.source === window.parent` and an HTTPS `event.origin`, then lock that exact origin for the frame session. Do not derive it from `document.referrer`; the Typeroll host intentionally applies a `no-referrer` policy to Extension iframes. An embedded app receives binding names in `typeroll.extension.init` but never receives the signed form tokens. It can request a submission with `typeroll.extension.form.submit`; the host responds with `typeroll.extension.form.result` after validating the frame, origin, installation, component, binding, and request IDs. For declared provider routes, the frame sends `typeroll.extension.api.request` with a unique `request_id`, relative `path`, and optional `method`, `headers`, and `body`. The site host performs the same direct, route-constrained request as `context.api.fetch()` and returns a serializable `typeroll.extension.api.result`. This browser-side message bridge does not relay traffic through Typeroll infrastructure or a hosting Function; the network request still goes straight from the published site to the provider API. Responses above 1 MiB fail closed. Next, implement the [provider backend and admin SSO](https://typeroll.com/docs/extensions/backend/). --- # Build an Extension Source: [Build an Extension](https://typeroll.com/docs/extensions/getting-started/) > Create, validate, install, and publish a Typeroll Extension from an external repository. An Extension lives in its own repository. A typical repository contains an Extension manifest, a browser entry module, an optional provider backend, and deployment configuration for the provider’s chosen platform. Use the [Typeroll Extension starter](https://github.com/Typeroll/extension-starter) as a GitHub template to get a working frontend, local host, provider example, manifest tooling, tests, and CI. ## Prerequisites [Section titled “Prerequisites”](https://typeroll.com/docs/extensions/getting-started/#prerequisites) * Node.js 22 or later; * a Typeroll organization and a site where you have admin permission; * an organization-scoped API key; * HTTPS origins for production assets and provider endpoints. In Typeroll, open **Developer → Extensions**, register a lowercase namespaced Extension ID, and copy the client secret when the Extension uses admin SSO. The secret is shown once and must remain server-side. ## Development workflow [Section titled “Development workflow”](https://typeroll.com/docs/extensions/getting-started/#development-workflow) 1. Add `typeroll-extension.json` to the repository root. 2. Implement a browser module that exports `mount(element, props, context)`. 3. Build the frontend into immutable JavaScript and CSS assets. 4. Put the exact SHA-256 asset hashes and public HTTPS URLs in the manifest. 5. Validate and upload a draft. 6. Install the draft on a development site owned by the same Typeroll organization and approve its requested scopes. 7. Publish a private or unlisted version, or submit a public version for review. The `@typeroll/mcp-server` package includes the `typeroll` developer CLI: ```sh export TYPEROLL_API_URL="https://your-admin.example.com" export TYPEROLL_API_KEY="your organization-scoped API key" npx --package @typeroll/mcp-server typeroll extension validate npx --package @typeroll/mcp-server typeroll extension push --draft npx --package @typeroll/mcp-server typeroll extension install --site your-site-id \ --config extension-config.json npx --package @typeroll/mcp-server typeroll extension configure --site your-site-id \ --installation your-installation-id --config extension-config.json npx --package @typeroll/mcp-server typeroll extension promote 1.0.0 ``` Local validation is a fast shape check. The portal remains authoritative for schema compatibility, trusted origins, permissions, configuration, and asset hash verification. `extension configure` merges the schema-defined values in the JSON file into the current installation config and queues a production deploy by default. Omitted values, including masked secrets, are preserved. Use `--no-deploy` only when applying several changes, then trigger one deploy after the last update. The installation ID is returned by `extension install` and is also shown under **Settings → Extensions**. Manifest v3 replaces the old gateway transport with a direct `api` contract. Provider requests must go straight from the browser to the provider backend; do not add a Typeroll relay or a customer Cloudflare Pages Function. For a backend-free lead tool, first create the receiving form in Typeroll, declare it under the component’s `form_bindings`, and request `forms:submit`. The form ID must match on the installation’s site. Its fields and post-submit email or webhook actions remain managed in the ordinary Forms admin. Draft and review versions can only be installed on sites owned by the Extension’s developer organization. Other organizations can only install a published version. You may change `distribution` while every saved version is still a draft; Typeroll updates those draft manifests and hashes together. Once any version has been published or submitted for review, distribution is locked. ## Add the component to a page [Section titled “Add the component to a page”](https://typeroll.com/docs/extensions/getting-started/#add-the-component-to-a-page) After installation, the component appears in the editor’s block picker. In HTML mode, use the provisioned block ID displayed under **Settings → Extensions**: ```html ``` This is an authoring directive, not a second script runtime. Preview and build replace it with the same inert mount shell used by a visual-editor block. ## Versioning rules [Section titled “Versioning rules”](https://typeroll.com/docs/extensions/getting-started/#versioning-rules) Published versions are immutable. Never replace asset bytes at a URL while reusing the same version and hashes. Release a new semantic version, upload it, and let Typeroll select it automatically for compatible installations. An installation is a timeless connection to the Extension ID. Its approved scopes never expand automatically: a new release can declare another scope, but it cannot use that scope until a site administrator grants it separately. Keep component IDs, props, configuration, and provider data backward compatible. Use a new Extension ID for a deliberately breaking product. If none of the published releases can run with the current runtime and stored configuration, Typeroll marks the Extension unavailable and omits it from the site build. Other pages and Extensions continue to build normally. Continue with the [frontend runtime and recipient-link contract](https://typeroll.com/docs/extensions/frontend/). --- # Extension manifest reference Source: [Extension manifest reference](https://typeroll.com/docs/extensions/manifest/) > Reference for Typeroll Extension manifest v3 fields, permissions, components, direct APIs, events, and data handling. The repository-root `typeroll-extension.json` is the source of truth for an Extension release. Manifest v3 uses these top-level fields: | Field | Required | Purpose | | ----------------------- | -------- | ------------------------------------------------------------------ | | `schema_version` | yes | Must be `3`. | | `id` | yes | Lowercase namespaced identifier, for example `com.example.quotes`. | | `name` | yes | Human-readable product name. | | `version` | yes | Immutable semantic version. | | `runtime_compatibility` | yes | Supported Typeroll runtime range. | | `distribution` | yes | `private`, `unlisted`, or `public`. | | `developer` | yes | Provider name, support URL, and privacy URL. | | `permissions` | yes | Requested site scopes with a reason for each scope. | | `auth` | no | Optional self-host issuer pairing endpoint. | | `config_schema` | no | Installation configuration and public/secret fields. | | `frontend` | no | Bundled components or embedded applications. | | `admin` | no | Provider-hosted admin pages. | | `api` | no | Provider-owned API and direct browser-call contract. | | `events` | no | Lifecycle subscriptions and signed webhook endpoint. | | `data_handling` | no | Personal-data, location, and retention disclosures. | ## Frontend entries [Section titled “Frontend entries”](https://typeroll.com/docs/extensions/manifest/#frontend-entries) A `bundled_component` declares HTTPS `script_url` and `script_sha256`, plus an optional style URL and matching hash. An `embedded_app` declares a frame URL and only the sandbox capabilities it needs. Each component can define a JSON schema for editor props, a restricted `url_context` declaration, and optional `form_bindings`: ```json { "id": "calculator", "label": "Quote calculator", "render_mode": "bundled_component", "form_bindings": [{ "id": "lead", "form_id": "quote-leads" }], "entry": { "script_url": "https://releases.example.com/quote/1.0.0/index.js", "script_sha256": "…" } } ``` Binding IDs are local to the component and are the names passed to `context.forms.submit()`. A binding can target only the fixed form ID declared in the immutable manifest version. ## Installation configuration [Section titled “Installation configuration”](https://typeroll.com/docs/extensions/manifest/#installation-configuration) `config_schema` is an object schema whose properties become installation settings. Mark a property `public: true` only when the browser component needs it; public values are embedded in preview and static build snapshots. A property with `format: "secret"` is encrypted at rest, never returned by read APIs, cannot be public, and cannot define a default. Use `enum` for a constrained value and a same-length `enum_labels` array for human-readable choices in the portal. Stored values remain stable identifiers: ```json { "type": "object", "additionalProperties": false, "properties": { "audience": { "type": "string", "title": "Audience", "enum": ["consumer", "business"], "enum_labels": ["Consumers", "Businesses"], "public": true } } } ``` Configuration updates merge declared keys into the existing installation. Omitted properties preserve their current values, including masked secrets. Because public config is build-time data, a config update requires a new site deploy. The developer CLI and MCP tool queue that deploy by default. ## Available scopes [Section titled “Available scopes”](https://typeroll.com/docs/extensions/manifest/#available-scopes) * `content:read`, `content:write` * `collections:read`, `collections:write` * `forms:read`, `forms:submit`, `forms:write` * `submissions:read` * `media:read`, `media:write` * `deploy:request` * `extension:config:read` The effective permission is the intersection of the manifest request, the site administrator’s grant, and the current Typeroll user’s permission. The portal rejects unknown scopes and routes. `forms:submit` is deliberately narrower than `forms:write`: it permits a component to submit to its declared bindings, but cannot create or edit forms or read submitted data. ## Direct provider API [Section titled “Direct provider API”](https://typeroll.com/docs/extensions/manifest/#direct-provider-api) `api.base_url` is the provider-owned HTTPS endpoint. Routes declare a path pattern and allowed HTTP methods. Paths are relative to `api.base_url`. `authentication` is `signed_installation` by default or `none` when the provider uses only its own public authentication. These declarations constrain the Typeroll runtime but do not replace provider-side authorization or CORS. Routes are unavailable in editor preview unless they declare `preview_methods`. That array must be a non-empty subset of the route’s ordinary `methods`. Expose only read-only or otherwise harmless operations: ```json { "path": "/quotes/*", "methods": ["GET", "POST"], "preview_methods": ["GET"] } ``` The runtime enforces this allowlist before sending a request. The signed preview token repeats the allowed routes, and the provider must enforce those claims server-side too. A preview token has `preview: true`; its `origin` claim and the opaque preview frame’s browser `Origin` header are both `null`, so their equality alone does not establish a trusted site origin. Typeroll never proxies these routes. The direct runtime always omits browser credentials and rejects redirects. A signed installation token, when enabled, uses `X-Typeroll-Extension-Token` and contains no visitor identity. ## Version lifecycle [Section titled “Version lifecycle”](https://typeroll.com/docs/extensions/manifest/#version-lifecycle) * `draft`: editable and available to its developer organization. * `review`: queued for hosted public-catalog review. * `published`: immutable and installable according to its distribution. * `deprecated`: existing timeless installations may keep using the release, but new installs are blocked; a newer compatible release is selected automatically when available. * `revoked`: the release is excluded from selection and catalog availability. The installation falls back to another compatible release when possible; otherwise its tokens, admin launch, and frontend are unavailable without blocking the rest of the site build. Download the complete [`typeroll-extension-manifest-v3.schema.json`](https://typeroll.com/docs/specs/typeroll-extension-manifest-v3.schema.json). The published file is generated from the same schema used by the portal. For production self-hosting variables and key rotation, see [Self-Hosting](https://typeroll.com/docs/guides/self-hosting/). --- # Extensions Source: [Extensions](https://typeroll.com/docs/extensions/overview/) > Add externally hosted frontend components and admin tools to Typeroll without moving application data or business logic into Typeroll. Typeroll Extensions let an external application contribute frontend blocks and admin pages to a Typeroll site. The provider keeps its source code, backend, business data, and deployment in its own AWS, GCP, Vercel, Cloudflare, or other environment. Use an Extension when you are building either: * a multi-tenant SaaS product that many Typeroll customers can install; or * a private, customer-specific application such as a wedding planner, quote generator, calculator, booking workflow, or customer portal. Both use the same versioned manifest and runtime. Their distribution policy is the only material difference. ## What an Extension can add [Section titled “What an Extension can add”](https://typeroll.com/docs/extensions/overview/#what-an-extension-can-add) * **Bundled components** that behave like native blocks in the editor. * **Embedded applications** for larger interactive frontend experiences. * **Admin pages** launched inside Typeroll with delegated user identity. * **Direct provider APIs** called by the frontend without a Typeroll or customer-hosted reverse proxy. * **Native lead tools** whose bespoke JavaScript and CSS submit into ordinary Typeroll Forms without a separate provider backend. * **Lifecycle webhooks** for install, update, disable, credential rotation, and uninstall events. Typeroll does not run or proxy arbitrary provider backend code. It installs an immutable manifest release, vendors hash-verified frontend assets into the site build, and issues narrowly scoped identity tokens for approved installations. The installation itself is a timeless connection: Typeroll follows the newest compatible published release, while the administrator-approved scopes remain fixed until an administrator explicitly changes them. The source code, manifest format, API routes, CLI, and product interface all use the term **Extension**. A premium product delivered through an Extension is a **Typeroll App**. ## Typeroll Apps and third-party products [Section titled “Typeroll Apps and third-party products”](https://typeroll.com/docs/extensions/overview/#typeroll-apps-and-third-party-products) Typeroll Apps is a separately sold premium collection operated only in Typeroll-controlled cloud accounts. Customers can buy and connect these apps whether their CMS is Typeroll Cloud or self-hosted. Self-hosting does not copy Typeroll Apps code or Functions into the operator’s infrastructure. Third-party SaaS and bespoke applications use the same Extension protocol but remain entirely in the developer’s accounts. Their frontend calls their API directly. Neither Typeroll nor the customer’s Cloudflare Pages project acts as a provider API proxy. ## Two frontend trust levels [Section titled “Two frontend trust levels”](https://typeroll.com/docs/extensions/overview/#two-frontend-trust-levels) A `bundled_component` is trusted site code. Typeroll downloads the exact hash-pinned JavaScript and CSS during deployment and publishes those immutable bytes under the customer site’s own `/_assets/extensions/…` paths. This avoids a mutable central runtime dependency and is ideal for customer-specific calculators, quizzes, lead magnets, and quote generators. Because that code executes on the customer origin, it has the same browser reach as other site JavaScript: DOM access, non-HttpOnly cookies, and web storage. Review the Extension connection and keep each release immutable. Use an `embedded_app` when the frontend should remain untrusted or needs isolation; it runs in a sandboxed provider-origin iframe instead. ## Hosted and self-hosted Typeroll [Section titled “Hosted and self-hosted Typeroll”](https://typeroll.com/docs/extensions/overview/#hosted-and-self-hosted-typeroll) The Extension runtime and private/unlisted development flow are part of the self-hostable platform. A self-hosted portal issues tokens and publishes JWKS from its own admin domain; an Extension must not assume `app.typeroll.com`. The premium Typeroll Apps implementations are not part of the open-source package and continue to run in Typeroll’s accounts. The hosted public catalog and its review process can remain a managed Typeroll service. Self-hosted installations do not contact that catalog unless an administrator explicitly imports or connects one. ## Security boundary [Section titled “Security boundary”](https://typeroll.com/docs/extensions/overview/#security-boundary) Provider frontend code never runs on the Typeroll admin origin. Admin pages run in a sandboxed frame on the provider origin and receive short-lived delegated identity through a server-to-server code exchange. A frontend block receives only public installation config, explicitly declared URL context, and capabilities approved for that installation. Form submission capabilities are limited to explicitly bound forms and do not grant submission reads or form administration. Recipient tokens in emailed links are owned by the provider. Typeroll can transport a declared opaque value to the component, but does not generate, validate, authorize, persist, or log that value. Next, [build your first Extension](https://typeroll.com/docs/extensions/getting-started/). --- # Reference architectures Source: [Reference architectures](https://typeroll.com/docs/extensions/reference-architectures/) > Choose a Typeroll Extension architecture and understand who owns each runtime. Typeroll supports the same Extension contract in Typeroll Cloud and in a self-hosted open-source installation. The deployment boundary depends on the product you are building, not on where Typeroll itself runs. | Reference | Use it for | Frontend | Application backend | Typeroll Forms | | ----------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------- | ---------------------------------- | | Public SaaS, native | A reusable SaaS product with UI that should feel native to the site | Versioned bundle copied to the customer domain during site deployment | Provider account | Optional | | Private bespoke, native | A customer-specific calculator, quiz, lead magnet, or quote generator | Versioned bundle copied to the customer domain during site deployment | None required | Canonical lead storage and actions | | Public SaaS, embedded | A reusable portal or workflow that needs a stronger isolation boundary | Provider-hosted sandboxed iframe | Provider account | Optional through the host bridge | The public [Extension starter repository](https://github.com/Typeroll/extension-starter) contains the full native contract: bundled blocks, Forms bindings, opaque recipient links, direct provider API calls, issuer pairing, lifecycle events, and admin SSO. Keep each production Extension in its own provider-owned repository even when several services share one tools domain and cloud project. ## Live production references [Section titled “Live production references”](https://typeroll.com/docs/extensions/reference-architectures/#live-production-references) * [Architecture gallery](https://autopilot-extension-demo.sites.typeroll.com/) * [Public SaaS as a native recipient-quote block](https://autopilot-extension-demo.sites.typeroll.com/quote/?quote=demo-customer-token) * [Private bespoke lead tool as a visual block](https://autopilot-extension-demo.sites.typeroll.com/bespoke-lead/) * [The same bespoke component from HTML mode](https://autopilot-extension-demo.sites.typeroll.com/bespoke-lead-html/) * [Public SaaS as a sandboxed iframe portal](https://autopilot-extension-demo.sites.typeroll.com/portal/?portal=demo-portal-token-2026) The opaque values in these URLs belong only to the public demo records. A real provider must generate high-entropy, scoped, expiring, and revocable recipient tokens. ## Public SaaS with a native block [Section titled “Public SaaS with a native block”](https://typeroll.com/docs/extensions/reference-architectures/#public-saas-with-a-native-block) Choose `bundled_component` when the provider wants a first-class, editable block and accepts that its reviewed JavaScript becomes customer-origin code. Typeroll fetches the declared immutable assets during site deployment, verifies their SHA-256 hashes, and publishes the verified copies below the site’s own `/_assets/extensions/` path. The browser calls the provider API directly. Typeroll supplies a short-lived, installation-scoped assertion only for manifest-allowlisted routes. The provider still owns visitor authentication, recipient tokens, data, replay rules, and every business action. Neither Typeroll nor a customer Cloudflare Function proxies these calls. ## Private bespoke block backed by Forms [Section titled “Private bespoke block backed by Forms”](https://typeroll.com/docs/extensions/reference-architectures/#private-bespoke-block-backed-by-forms) A customer-specific lead tool normally needs only a private `bundled_component`, the `forms:submit` scope, and an explicit form binding. The repository owns the creative JavaScript and CSS; the installed site’s ordinary Forms module owns submissions, inbox access, email actions, and webhooks. This pattern needs no provider API, client credential, lifecycle webhook, customer-hosted Function, or separate lead database. It works unchanged in Cloud and self-hosted installations because each installation submits to its own Forms API. Forms webhooks can update an external CRM or register after the canonical submission is stored. ## Public SaaS in an embedded app [Section titled “Public SaaS in an embedded app”](https://typeroll.com/docs/extensions/reference-architectures/#public-saas-in-an-embedded-app) Choose `embedded_app` when the provider wants to deploy the UI continuously from its own account or should not receive customer-origin trust. Typeroll creates a sandboxed iframe with `allow-scripts allow-same-origin`, sends a versioned init message, and exposes narrow message bridges for resizing, internal navigation, declared Forms bindings, and allowlisted provider API routes. The frame should accept initialization only from `window.parent` over HTTPS, lock that exact origin, and validate source window, origin, protocol version, installation ID, and component ID on every subsequent message. It must not depend on `document.referrer`: Typeroll intentionally uses a no-referrer policy. The iframe never receives Typeroll’s signed installation assertion. For API calls it asks the host to perform the same direct, route-constrained request used by a native component. An embedded app can keep a consumed recipient token in frame memory while users move between internal views. Those views do not require separate Typeroll pages or paths. The provider remains responsible for generating a cryptographically random, scoped, expiring, and revocable token and for binding every record and action to the verified installation. ## Ownership boundary [Section titled “Ownership boundary”](https://typeroll.com/docs/extensions/reference-architectures/#ownership-boundary) A Typeroll site deployment contains static pages, the runtime, verified native Extension assets, and iframe descriptors. It does not deploy third-party application functions. * Typeroll Cloud operates only Typeroll Cloud services. * A self-hosted operator runs only their open-source Typeroll installation. * Extension developers run their own SaaS or bespoke backend, if one exists. * Premium Typeroll Apps are operated only in Typeroll’s accounts and can be sold to both Cloud and self-hosted customers. * Customer static hosting never receives an application proxy merely because an Extension is installed. Next, see [Frontend and recipient links](https://typeroll.com/docs/extensions/frontend/) for the runtime API and [Provider backend and admin SSO](https://typeroll.com/docs/extensions/backend/) for signed requests, pairing, and delegated admin access. --- # AI agent and MCP client compatibility Source: [AI agent and MCP client compatibility](https://typeroll.com/docs/getting-started/client-compatibility/) > Connection options and Typeroll verification status for Claude Desktop, Claude Code, Cursor, VS Code, ChatGPT, Cline and Zed. Typeroll CMS connects to AI agents through MCP and the REST API. It does not require a particular AI model or client. An MCP client must support the transport, authentication and tool calls used by the connection you choose. ## Connection options [Section titled “Connection options”](https://typeroll.com/docs/getting-started/client-compatibility/#connection-options) * **Remote:** MCP Streamable HTTP at `https://app.typeroll.com/api/mcp`, or `https:///api/mcp` for self-hosting. Use OAuth consent or an explicit bearer header where the client supports it. * **Local:** launch `npx -y @typeroll/mcp-server` over stdio with the environment variables in [Connect your AI agent](https://typeroll.com/docs/getting-started/mcp-server/#local-mcp-process-stdio). A stdio process targets one Site; set `TYPEROLL_SITE_ID` when the key can access multiple sites. * **Direct API:** an agent can call the authenticated REST API without MCP. Reading the [public documentation](https://typeroll.com/docs/llms.txt) requires neither a key nor MCP. A client that only supports older HTTP+SSE connections is not automatically compatible with the Streamable HTTP endpoint. Configuration schemas, available features and workspace permissions also differ between clients. ## Client status [Section titled “Client status”](https://typeroll.com/docs/getting-started/client-compatibility/#client-status) Provider documentation reviewed on **September 11, 2026**. The connection options below are based on those providers’ documentation. **A version-specific Typeroll end-to-end test has not yet been recorded for these named clients.** They are connection candidates, not a list of certified integrations. | Client | Connection to configure | Typeroll client test | | ------------------------------------------------------------------------------------------------ | -------------------------------------------------- | -------------------- | | [Claude Desktop](https://typeroll.com/docs/getting-started/client-compatibility/#claude-desktop) | Remote custom connector | Not yet recorded | | [Claude Code](https://typeroll.com/docs/getting-started/client-compatibility/#claude-code) | Remote HTTP or local stdio | Not yet recorded | | [Cursor](https://typeroll.com/docs/getting-started/client-compatibility/#cursor) | Remote HTTP or local stdio | Not yet recorded | | [VS Code](https://typeroll.com/docs/getting-started/client-compatibility/#vs-code) | Remote HTTP or local stdio in its agent tools | Not yet recorded | | [ChatGPT](https://typeroll.com/docs/getting-started/client-compatibility/#chatgpt) | Remote custom MCP app, subject to workspace access | Not yet recorded | | [Cline](https://typeroll.com/docs/getting-started/client-compatibility/#cline) | Remote Streamable HTTP or local stdio | Not yet recorded | | [Zed](https://typeroll.com/docs/getting-started/client-compatibility/#zed) | Remote or local context server | Not yet recorded | Typeroll’s automated checks exercise its server tools and OAuth implementation. Those checks do not substitute for completing authentication and tool calls in a particular third-party application. ### Claude Desktop [Section titled “Claude Desktop”](https://typeroll.com/docs/getting-started/client-compatibility/#claude-desktop) Use a remote custom connector with the hosted endpoint. The remote connection is made from the provider’s infrastructure, so the portal must be reachable there; your computer’s `localhost` is not a public server address. Follow [Claude’s custom connector instructions](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp). ### Claude Code [Section titled “Claude Code”](https://typeroll.com/docs/getting-started/client-compatibility/#claude-code) Claude Code documents both HTTP and stdio MCP connections. Use its MCP setup command for the selected transport and its authentication flow for a remote server. Follow [Claude Code’s MCP guide](https://code.claude.com/docs/en/mcp). Typeroll’s optional `init` command currently creates `.mcp.json` and `.claude/skills/` files. That scaffold is client-specific; other agents can use the MCP connection and `read_guide`, `list_skills` and `read_skill` without it. ### Cursor [Section titled “Cursor”](https://typeroll.com/docs/getting-started/client-compatibility/#cursor) Use Cursor’s MCP settings and its own `mcp.json` schema. Its documentation covers stdio commands, remote connections and OAuth. Follow [Cursor’s MCP guide](https://prod.cursor.com/docs/mcp); do not assume a different editor’s configuration file can be copied unchanged. ### VS Code [Section titled “VS Code”](https://typeroll.com/docs/getting-started/client-compatibility/#vs-code) Use VS Code’s MCP server management for agent tools. Its configuration uses a `servers` map, which differs from clients using `mcpServers`. Follow [VS Code’s MCP setup guide](https://code.visualstudio.com/docs/agent-customization/mcp-servers) for adding a server and approving access. ### ChatGPT [Section titled “ChatGPT”](https://typeroll.com/docs/getting-started/client-compatibility/#chatgpt) Use a custom remote MCP app where your account and workspace permit it. Check read and write tool availability separately: access to a connector does not establish that editing and deployment tools are enabled. Developer-mode and administrator requirements vary by plan. Follow [ChatGPT’s MCP app instructions](https://help.openai.com/en/articles/12584461-developer-mode-and-full-mcp-connectors-in-chatgpt-beta). The stdio npm command is not a remote app URL. ### Cline [Section titled “Cline”](https://typeroll.com/docs/getting-started/client-compatibility/#cline) Cline documents both stdio and remote connections. For the hosted Typeroll endpoint, explicitly select **Streamable HTTP**; do not leave it on the legacy SSE transport. Its remote configuration supports bearer headers. Follow [Cline’s MCP guide](https://docs.cline.bot/mcp/mcp-overview). ### Zed [Section titled “Zed”](https://typeroll.com/docs/getting-started/client-compatibility/#zed) Zed calls these connections context servers and configures them under `context_servers`. It documents local commands, remote URLs, bearer headers and OAuth when no Authorization header is configured. Follow [Zed’s MCP guide](https://zed.dev/docs/ai/mcp). ## Verify your connection [Section titled “Verify your connection”](https://typeroll.com/docs/getting-started/client-compatibility/#verify-your-connection) Start with a read-only request: identify the accessible Site and Version, read the site capabilities and read a page. For remote multi-site connections, use `list_sites` to choose the Site; a stdio connection is bound at startup. If you want to verify editing, use a disposable test Site. Create a draft, read it back, make a small change and inspect its preview. Test saving, deletion and publishing only when you intend those actions. A successful read alone does not prove that writes, authentication renewal or deployment work in that client. When recording a verified client, include the client version, OS, Typeroll version and environment, transport, authentication method, test date and actual operations exercised. Record known restrictions alongside the result. Never include keys, tokens or private customer content in a compatibility report. --- # Your first site Source: [Your first site](https://typeroll.com/docs/getting-started/first-site/) > Create a site, edit or generate content, review a preview, and publish the saved version from Typeroll CMS. ## 1. Create a site [Section titled “1. Create a site”](https://typeroll.com/docs/getting-started/first-site/#1-create-a-site) In the portal, choose the **Organization** and select **New site**. You can also ask an [authenticated AI agent](https://typeroll.com/docs/getting-started/mcp-server/) to create one using an organization-scoped key. A site-scoped key cannot create other sites. You do not need GitHub, Cloudflare or a custom domain to start editing. ## 2. Add content [Section titled “2. Add content”](https://typeroll.com/docs/getting-started/first-site/#2-add-content) Choose the Site, open **Pages** and create a page. Open it in the [visual editor](https://typeroll.com/docs/guides/the-editor/) to add blocks and edit their fields. With an AI agent, describe the site and the work you want it to do. For example: ```text Create a small website for a landscape design studio: Home, Projects and Contact. Use our existing site settings, header and footer. Build editable blocks. Keep the pages as drafts and show me a preview before publishing. ``` The agent uses your site’s settings, blocks, templates and API tools. If it works with multiple sites, have it confirm the selected Site and Version before changes. The bundled `tr-new-site` recipe is optional; clients load recipe files differently. ## 3. Review and save [Section titled “3. Review and save”](https://typeroll.com/docs/getting-started/first-site/#3-review-and-save) Open the editor’s **Publish** menu and use **Review changes** to compare the working copy with saved content. Select **Save** when the changes are ready. For an agent-created preview, request `include_working_copy: true` to include unsaved changes. A normal preview shows saved CMS content, which can differ from the last public deployment. Expiring previews do not publish a permanent site. Check navigation, text, image alt text and the mobile layout. Use the editor to correct content or ask the agent to make a specific change, then save it. ## 4. Set up publishing [Section titled “4. Set up publishing”](https://typeroll.com/docs/getting-started/first-site/#4-set-up-publishing) Open **Account → Publishing**. An organization owner or admin completes the [account and domain setup](https://typeroll.com/docs/guides/customer-publishing/) and selects the build provider. The Site uses its assigned Hosting Group for static hosting. The site’s default address is under that group’s configured site address base. New customer-publishing sites do not get a permanent `typeroll.com` subdomain. ## 5. Publish the saved version [Section titled “5. Publish the saved version”](https://typeroll.com/docs/getting-started/first-site/#5-publish-the-saved-version) Select the intended **Version**. In **Publish**, set ready pages to **Published**, then select **Deploy**. Review the changes listed beneath the deploy control. The agent equivalent is to save content, set its status and call `trigger_deploy`. Wait while the build and distribution are verified. The public link appears automatically when the deployed artifact is available. A saved **Published** status alone does not mean the page is live. The `main` version is the live branch; other versions use separate branch addresses with `noindex`. See [Deploy tools](https://typeroll.com/docs/tools/deploy/). ## Next steps [Section titled “Next steps”](https://typeroll.com/docs/getting-started/first-site/#next-steps) * [Prepare your website and media domains](https://typeroll.com/docs/publishing/domains/). * [Add Forms](https://typeroll.com/docs/apps/forms/) and verify a submission before sharing the site. * [Check SEO and indexing](https://typeroll.com/docs/technical/seo-and-indexing/). * [Run the CMS yourself](https://typeroll.com/docs/guides/self-hosting/). --- # Introduction Source: [Introduction](https://typeroll.com/docs/getting-started/introduction/) > Learn how Typeroll CMS combines visual editing, AI-agent access and static publishing, and choose where to start. Typeroll CMS is an open-source CMS for static websites. Edit content in your browser or connect your own AI agent through the REST API and MCP server. Self-host the CMS or use Typeroll Cloud. Freelancers and web agencies can manage multiple client sites while clients edit content in the browser. Organizations can also use it for their own websites. ## The three parts [Section titled “The three parts”](https://typeroll.com/docs/getting-started/introduction/#the-three-parts) | Part | What it does | | --------------------- | ------------------------------------------------------------------------------------------------------------ | | CMS and visual editor | Store pages, blocks, templates, media and settings. Review changes and save content. | | API and MCP | Let your AI agent read and change the same content, within its credentials’ scope. | | Static publishing | Freeze a version, generate source in GitHub, build it and upload static files to the site’s hosting account. | The CMS is the editing source of truth. Generated site repositories are portable build inputs, but manual changes in them are unsupported and may be overwritten. ## Start without publishing accounts [Section titled “Start without publishing accounts”](https://typeroll.com/docs/getting-started/introduction/#start-without-publishing-accounts) Create a site, edit pages and use expiring previews before connecting GitHub, Cloudflare or a domain. Public deployment stays locked until the required publishing setup is ready. Saving a page and changing its status do not by themselves update a public website. * [Use the visual editor](https://typeroll.com/docs/guides/the-editor/) to work in your browser. * [Connect an AI agent](https://typeroll.com/docs/getting-started/mcp-server/) for API or MCP workflows. * [Build your first site](https://typeroll.com/docs/getting-started/first-site/) for an end-to-end example. ## When you are ready to publish [Section titled “When you are ready to publish”](https://typeroll.com/docs/getting-started/introduction/#when-you-are-ready-to-publish) An Organization owns the GitHub connection, shared media and build-provider selection. Each Hosting Group supplies a Cloudflare hosting account and site address base. A Site has its own generated repository, static Pages project, version branches and optional website and media domains. Build execution and static hosting are separate. A shared Cloudflare build engine or GitHub Actions builds sites for the organization; finished files go to each site’s Hosting Group. See [Publishing](https://typeroll.com/docs/guides/customer-publishing/). Forms, Apps and Extensions may depend on runtime services after deployment. [Self-hosting](https://typeroll.com/docs/guides/self-hosting/) covers running the CMS and core runtime yourself. --- # Connect your AI agent Source: [Connect your AI agent](https://typeroll.com/docs/getting-started/mcp-server/) > Connect an MCP-compatible AI agent to Typeroll CMS through the hosted MCP endpoint or a local stdio process. Typeroll CMS exposes the same site-management tools through hosted MCP and a local MCP process. Use an AI agent that supports one of these transports, or integrate directly with the REST API. The visual editor also works without an agent. See [Client compatibility](https://typeroll.com/docs/getting-started/client-compatibility/) for connection options and recorded test status. ## Let your agent read the documentation [Section titled “Let your agent read the documentation”](https://typeroll.com/docs/getting-started/mcp-server/#let-your-agent-read-the-documentation) Give your agent the [documentation index](https://typeroll.com/docs/llms.txt). It links to a plain-text version of each page, the [complete documentation](https://typeroll.com/docs/llms-full.txt), and a [shorter set](https://typeroll.com/docs/llms-small.txt). All files are generated from these same docs. They include absolute links and source addresses, preserve code examples and can be fetched without JavaScript, a login or an API key. Reading documentation is public. Managing your actual Sites through MCP or the API still requires the scoped credentials described below. ## Choose credentials [Section titled “Choose credentials”](https://typeroll.com/docs/getting-started/mcp-server/#choose-credentials) In the portal, open the menu, choose the correct **Organization**, then go to **Account → API keys**. Create an organization key when the agent needs to manage multiple accessible sites or organization publishing settings. For a single site, open that Site’s **Settings → API keys** under **External access**. Site keys stay within that site’s permission scope. Domain changes require site admin access; organization connections require an organization key. Copy the key when it is shown and store it in your agent’s private credential settings. Do not commit a real key to a repository. Revoke it from the same page when you no longer want it to grant access. ## Hosted MCP: connect with a URL [Section titled “Hosted MCP: connect with a URL”](https://typeroll.com/docs/getting-started/mcp-server/#hosted-mcp-connect-with-a-url) Use this option if your client supports **MCP Streamable HTTP with OAuth**. No local Node.js installation is required. 1. Add a remote MCP server in your agent client’s integration settings. 2. Enter `https://app.typeroll.com/api/mcp` for Typeroll Cloud, or `https:///api/mcp` for a self-hosted portal. 3. Complete the Typeroll consent screen with the API key and select **Grant access**. 4. Return to the client and confirm that Typeroll’s tools are available. Client menus and supported authentication methods vary. Use your client’s MCP instructions for adding a server. The hosted endpoint supports organization and site keys; the underlying key’s permissions continue to apply after consent. Revoking it also invalidates the issued authorization on subsequent checks. For a client that supports explicit bearer headers instead of OAuth, configure `Authorization: Bearer ` in its private connection settings. Never put the key in the server URL. ## Local MCP process: stdio [Section titled “Local MCP process: stdio”](https://typeroll.com/docs/getting-started/mcp-server/#local-mcp-process-stdio) Use this option if your agent can launch a local process and communicate over stdin/stdout. Install the Node.js version required by the current [`@typeroll/mcp-server` package](https://www.npmjs.com/package/@typeroll/mcp-server) (Node.js 20 or later for the current standalone MCP package). Configure these values in the client: | Setting | Value | | ------------------------------- | ------------------------------------------------- | | Command | `npx` | | Arguments | `-y`, `@typeroll/mcp-server` | | Environment: `TYPEROLL_API_URL` | `https://app.typeroll.com`, or your portal origin | | Environment: `TYPEROLL_API_KEY` | Your private API key | A stdio process targets one Site. If the key can access several Sites, set `TYPEROLL_SITE_ID` to the intended Site ID; a site-scoped key can discover its one Site automatically. Hosted multi-site connections select a Site per call. A JSON-style client configuration can look like this. The exact configuration file and schema depend on the client; the placeholder is not a working key. ```json { "mcpServers": { "typeroll": { "command": "npx", "args": ["-y", "@typeroll/mcp-server"], "env": { "TYPEROLL_API_URL": "https://app.typeroll.com", "TYPEROLL_API_KEY": "" } } } } ``` Use the client’s credential store or environment injection if available. Reload or restart its MCP connection after changing the configuration. Avoid duplicate Typeroll entries in different configuration scopes. ## Verify the connection [Section titled “Verify the connection”](https://typeroll.com/docs/getting-started/mcp-server/#verify-the-connection) Ask your agent: ```text List the Typeroll sites available to this connection. Do not change anything. ``` A hosted multi-site connection can call `list_sites`. A stdio connection should read its bound Site with `get_site`. Confirm the Site and Version before asking for edits. Edits normally create working copies. Ask the agent to show the changes, save approved content and publish only when requested. See [Drafts and saving](https://typeroll.com/docs/tools/drafts-and-saving/) and [Deploy tools](https://typeroll.com/docs/tools/deploy/). ## Optional workflow recipes [Section titled “Optional workflow recipes”](https://typeroll.com/docs/getting-started/mcp-server/#optional-workflow-recipes) The MCP package includes Markdown recipes for tasks such as creating a site, importing content and checking SEO. Install them into a local directory: ```bash npx @typeroll/mcp-server install-skills ./typeroll-skills ``` Point your agent at the relevant recipe, or adapt its installation to your client’s instructions. A directory name alone does not make every client load these files automatically. The MCP connection itself does not require recipes. Existing files are skipped by default. `--force` replaces them with the bundled versions, so keep your own instructions separately if you want to preserve them. See [Skills and recipes](https://typeroll.com/docs/skills/overview/). ## REST API without MCP [Section titled “REST API without MCP”](https://typeroll.com/docs/getting-started/mcp-server/#rest-api-without-mcp) An agent or script can call the bearer-authenticated API under `/api/v1` directly. Use an API key in the `Authorization` header. Choose organization or site scope for the intended operation; an AI agent with its own DNS-provider access can apply returned DNS requirements and ask Typeroll to verify the result. See [Publishing with your own accounts](https://typeroll.com/docs/guides/customer-publishing/#api-and-mcp). ## Troubleshooting [Section titled “Troubleshooting”](https://typeroll.com/docs/getting-started/mcp-server/#troubleshooting) | Result | What to check | | ------------------------- | ---------------------------------------------------------------------------------------------- | | Missing bearer token | The key is missing from the connection headers or local process environment. | | Invalid or revoked token | Use a current key from the same portal environment. Staging and production keys are separate. | | Site not found | Call `list_sites` and confirm the selected site is accessible to this key. | | Write permission required | A read-only share or key cannot edit or deploy. Ask the site owner to grant the needed access. | | Organization key required | Site-scoped keys cannot change organization connections or domains. | | No tools shown | Confirm the transport and authentication support, then reload the client’s MCP connection. | | Local process exits | Verify Node.js, `npx`, the package installation and the two required environment variables. | A hosted client must be able to reach your portal over HTTPS. For local self-hosting development, use a client that can reach the local endpoint or use stdio. Do not assume a remotely hosted agent can reach your computer’s localhost. --- # Core module documentation has moved Source: [Core module documentation has moved](https://typeroll.com/docs/guides/apps-and-analytics/) > Find the built-in module overview and individual module references. Built-in CMS capabilities now have their own core-module reference section. They are distinct from separately sold Typeroll Apps, which run through the Extension platform. * Start with [Core modules overview](https://typeroll.com/docs/apps/overview/). * Read the [Analytics module reference](https://typeroll.com/docs/apps/analytics/). * Configure [Analytics attribution](https://typeroll.com/docs/apps/funnel-attribution/) and [first-party conversion events](https://typeroll.com/docs/apps/events/) as Analytics modules. * Use [Recipes](https://typeroll.com/docs/recipes/booking-link-attribution/) for narrower implementations built from general apps. --- # Custom Domain Source: [Custom Domain](https://typeroll.com/docs/guides/custom-domain/) > Connect your own domain to a Typeroll site on Cloudflare Pages. This page describes the legacy managed publishing mode. For customer-owned GitHub, Cloudflare and R2, use [Publishing with your own accounts](https://typeroll.com/docs/guides/customer-publishing/). New customer-publishing sites do not receive a permanent Typeroll subdomain. Legacy managed sites may have a subdomain at `yoursite.sites.typeroll.com`. To use your own domain (e.g. `acme.se`), connect it in the portal and update your DNS. 1. **Open the portal** Go to [app.typeroll.com](https://app.typeroll.com/) → your site → **Settings → Domain**. 2. **Enter your domain (this republishes your site)** Type your domain (e.g. `acme.se` or `www.acme.se`) and click **Connect** with **Publish now** checked (the default). This is the step that makes your domain the canonical address: Typeroll immediately republishes so your sitemap, `canonical_url`, OG tags, and internal links all use it. **You don’t need DNS in place yet** — that’s the last step. The portal then shows you the DNS records to add. 3. **Add DNS records (the last step)** Log in to your DNS provider (Cloudflare, GoDaddy, Namecheap, etc.) and add the records shown. Typically: | Type | Name | Value | | ----- | ------------ | ------------------------ | | CNAME | `@` or `www` | `your-project.pages.dev` | If your DNS provider doesn’t support CNAME on the root (`@`), use an ANAME/ALIAS record instead, or point to a `www` subdomain with a redirect. 4. **Wait for propagation** DNS changes can take a few minutes to a few hours. The portal shows a green checkmark when the domain is verified and SSL is provisioned — at that point your site is reachable on the domain it already claims as canonical. (If you skipped “Publish now” in step 2, trigger a deploy now so the build picks up the domain.) Your dashboard tracks the same thing: the badge on the site card reads **DNS pending** while you’re waiting and flips to **live** once the domain is verified. **DNS failed** means the check ran and didn’t find your records — worth re-checking the CNAME before waiting any longer. ## API and MCP automation [Section titled “API and MCP automation”](https://typeroll.com/docs/guides/custom-domain/#api-and-mcp-automation) The bearer-authenticated domain route and MCP `add_domain` tool follow the same domain-first workflow as the portal. They accept the hostname, optional `prefer: "apex" | "www"`, and `auto_deploy` (default `true`). The response includes the DNS target and, when publishing was requested, the deploy job id. After that deploy succeeds, point DNS and call `poll_domain` until the domain is verified. Do not call `activate_domain` in new automation; it remains only for compatibility with old records and clients. ## Subdomain vs root domain [Section titled “Subdomain vs root domain”](https://typeroll.com/docs/guides/custom-domain/#subdomain-vs-root-domain) | Setup | Example | Notes | | ------------- | ---------------- | ---------------------------------------- | | Root domain | `acme.se` | Requires ANAME/ALIAS or Cloudflare Proxy | | www subdomain | `www.acme.se` | CNAME works everywhere | | Subdomain | `studio.acme.se` | CNAME works everywhere | For root domains with a registrar that doesn’t support ANAME/ALIAS, the easiest solution is to use `www` as the canonical domain and redirect `acme.se` → `www.acme.se` at the DNS level. ## apex + www — Typeroll handles the pair for you [Section titled “apex + www — Typeroll handles the pair for you”](https://typeroll.com/docs/guides/custom-domain/#apex--www--typeroll-handles-the-pair-for-you) When you enter an apex (`acme.se`) or www (`www.acme.se`) domain in the portal, Typeroll treats it as a **pair** and does the wiring automatically: * **Registers BOTH variants** on the Cloudflare Pages project, so neither one returns a 522 (the classic “works for me, broken for customers who typed the other variant” trap). * **Picks one as canonical** — apex by default; you can switch to www in the connect dialog. All canonical URLs, OG tags, and sitemaps use the canonical variant. * **Emits a 301 from the other variant → canonical**, path preserved, into the build’s `_redirects` file (e.g. `https://acme.se/blog/ → https://www.acme.se/blog/`). ### Your only job: point DNS for both variants [Section titled “Your only job: point DNS for both variants”](https://typeroll.com/docs/guides/custom-domain/#your-only-job-point-dns-for-both-variants) | Type | Name | Target | Proxy | | ----- | ----- | ------------------------ | -------- | | CNAME | `www` | `your-project.pages.dev` | DNS only | | CNAME | `@` | `your-project.pages.dev` | DNS only | If your registrar doesn’t support CNAME on `@`, use an ANAME/ALIAS record there instead. Both variants must resolve before the pair fully verifies — the portal tracks each separately. ### Choosing apex vs www as canonical [Section titled “Choosing apex vs www as canonical”](https://typeroll.com/docs/guides/custom-domain/#choosing-apex-vs-www-as-canonical) Some sites — especially those migrated from WordPress — prefer `www.acme.se` as the canonical. Enter `www.acme.se` (or pick **www** in the connect dialog) and every generated canonical URL, OG tag, and sitemap entry references the www version, with `acme.se` 301-ing to it. ## SSL [Section titled “SSL”](https://typeroll.com/docs/guides/custom-domain/#ssl) SSL certificates are provisioned automatically by Cloudflare Pages. No configuration needed. --- # Publishing with your own accounts Source: [Publishing with your own accounts](https://typeroll.com/docs/guides/customer-publishing/) > Connect GitHub, Cloudflare, domains and media once per organization, then publish static sites from Typeroll. ## Start editing before connecting accounts [Section titled “Start editing before connecting accounts”](https://typeroll.com/docs/guides/customer-publishing/#start-editing-before-connecting-accounts) You can create sites, connect an AI agent, edit pages, save drafts and share an expiring preview before setting up publishing. With customer publishing enabled, new uploads use the hosting operator’s private draft storage until your R2 access is ready. Public deployment stays locked and links to **Publishing** with the missing setup steps. A public website address appears after the exact deployment has been verified at that address. Your hosting operator must enable customer publishing and configure private draft storage. Existing managed sites retain their existing publishing mode until they are explicitly migrated. ## Organization setup [Section titled “Organization setup”](https://typeroll.com/docs/guides/customer-publishing/#organization-setup) Open the sidebar, select the correct **Organization**, then select **Publishing** under **Account**. On mobile, open the menu first. Organization owners and admins manage these connections. 1. Select **Connect GitHub**, sign in as an owner of your GitHub organization, and authorize Typeroll. If prompted, install the Typeroll publisher GitHub App with **All repositories**, then return to connect. Choose an organization only if more than one is available. No organization name or ID needs to be typed. 2. Select **Connect Cloudflare**, sign in and approve the account. Optional DNS and URL rewrite permissions let Typeroll configure domains automatically. You can decline these and let your administrator or AI agent manage DNS. 3. Typeroll checks R2 automatically. If activation is required, follow the account link to **Storage & databases → R2 object storage → Overview** and complete Cloudflare’s subscription checkout. Return and select **I’ve activated R2 — check again**. Typeroll creates separate private-original and public-media buckets and configures browser uploads. 4. In **R2 object storage → Overview → Account Details → API Tokens → Manage**, create an **Account API token** with **Object Read & Write** for **both buckets shown in Typeroll**. Enter its **Access Key ID** and **Secret Access Key**, then select **Verify keys and finish setup**. These are different from the API token value. A Super Administrator can create account tokens; a User API token is an alternative whose access depends on that user’s continued membership. 5. In **Builds**, select the organization’s build provider and complete its shared-engine setup. Cloudflare builds need a one-time builder repository connection and build token; GitHub Actions uses the existing Typeroll App connection. See [Cloudflare hosting and builds](https://typeroll.com/docs/publishing/cloudflare/). 6. Under **Organization domains**, choose a **Cloudflare domain** from the connected account. Enter short labels in **Media subdomain** and **Sites subdomain**, such as `media` and `sites`. The complete addresses appear below the fields. Select **Configure domains** to connect the media hostname to R2 and queue media migration. HTTPS activation is checked automatically. Each site and version gets its own website address when you publish. Use the refresh icon beside **Cloudflare domain** to retrieve newly added domains. Refreshing keeps the existing account connection. An empty result does not mean you need to reconnect. If the saved OAuth grant lacks domain permissions, **Allow domain access** appears. It opens Cloudflare’s approval screen and keeps the existing account and R2 setup. Typeroll will not replace an existing DNS destination or an existing shared media hostname through this setup action. For external DNS or agent-managed settings, open **Manual settings or external DNS**. Enter the complete **Site address base** and **Shared media host**, choose **Me or my AI agent**, and save. Read **Setup instructions**, apply the records, then select **Check domain status**. Each Hosting Group owns a Cloudflare hosting account and a site address base. Default reuses the organization connection. The Organization owns shared media, DNS access, GitHub and the build-provider selection. Each site gets its own generated private repository and static Pages project. Adding a site or version reuses the organization connections. Account ID is only needed in advanced token setup. In Cloudflare, select the account and use **Search → Copy account ID**, or **Workers & Pages → Account Details → Account ID**. Copy the 32-character account value, not a Zone ID. ### Approve a GitHub permission update [Section titled “Approve a GitHub permission update”](https://typeroll.com/docs/guides/customer-publishing/#approve-a-github-permission-update) An existing connection does not need to be disconnected or connected again. In **Publishing → GitHub account**, select **Approve GitHub update** when it appears. This opens the existing installation in your GitHub organization. Review and accept the requested permissions as a GitHub organization owner, then return to Typeroll. Typeroll checks automatically and confirms when the permissions are up to date. **Check GitHub permissions** repeats the check. If Typeroll says it needs to enable GitHub build access first, your hosting operator must update the publisher App registration before an approval is available. You do not need to edit an App, create a token or reconnect. Your existing connection remains active. Permission approval alone does not change the selected build provider. ### When DNS is hosted elsewhere [Section titled “When DNS is hosted elsewhere”](https://typeroll.com/docs/guides/customer-publishing/#when-dns-is-hosted-elsewhere) R2 media domains must belong to a zone in the same Cloudflare account as the bucket. You do not need to move your root website, email or unrelated subdomains. Cloudflare’s partial CNAME setup connects selected hostnames while keeping your existing authoritative DNS provider and nameservers. It requires Business or Enterprise. Selecting external DNS management in Typeroll means you or your agent manage the records, including records in Cloudflare; it does not bypass this provider requirement. If your domain already uses Cloudflare DNS, keep other records pointing at their existing services. A CNAME pointing to an `r2.dev` address is unsupported. See [R2 custom domains](https://developers.cloudflare.com/r2/buckets/public-buckets/) and [partial setup availability](https://developers.cloudflare.com/dns/zone-setups/partial-setup/). ### Read status and complete setup [Section titled “Read status and complete setup”](https://typeroll.com/docs/guides/customer-publishing/#read-status-and-complete-setup) **Shared media domain status** distinguishes missing Cloudflare access, unfinished R2 setup, an unattached or disabled media domain, pending ownership/certificate activation, and an active Cloudflare media domain. Provider errors are shown as an unknown status, not as proof that a working domain is disconnected. The panel names the connected account and public bucket and records when it was checked. For manual setup, open **Cloudflare → R2 object storage → the public bucket → Settings → Custom Domains → Add**. Enter the shared media hostname, select **Continue**, review the record and select **Connect Domain**. Use **Manage DNS** in the connected domain’s menu to view the record. With partial setup, also apply Cloudflare’s supplied verification and CNAME records at your DNS provider. The UI links to Cloudflare’s current instructions; do not guess a target or point the hostname at the S3 API endpoint. An active media-domain status does not verify every site address or publish a site. Website and version addresses have their own DNS requirements and public verification in the site’s Publishing flow. The address base itself does not serve a website; generated addresses are below it. Deployment verifies actual published media files before exposing the live link. The organization API `GET /api/v1/publishing/domains` and MCP `read_organization_publishing_domains` return the same `domain_status`, including `state`, `checked_at`, `zone_check`, certificate/ownership state and structured `steps`. Set `sites_domain` and `media_host` independently using the corresponding PUT route or `set_organization_publishing_domains`. Legacy `default_domain` configurations retain their old addresses until explicitly updated. Changing the site address base affects subsequent deployments; an existing shared media host cannot be replaced by a plain settings overwrite. To use the same automatic setup from an agent: 1. Call `GET /api/v1/publishing/zones` or MCP `list_organization_publishing_domains` to list available domains. 2. Read the current configuration revision from the domains endpoint. 3. Call `POST /api/v1/publishing/domains` or `configure_organization_publishing_domains` with `revision`, `zone_id`, `media_subdomain` and `sites_subdomain`. The labels contain no dots or URL. 4. Poll the domains endpoint while `domain_status.state` is `pending`. A `setup_error` means settings were saved but provider setup needs attention; use the returned revision when retrying. These endpoints require an organization API key. Site keys cannot discover or configure organization domains. Setup creates the selected R2 media connection; it does not publish a website or modify unrelated DNS records. ## Publish a site or version [Section titled “Publish a site or version”](https://typeroll.com/docs/guides/customer-publishing/#publish-a-site-or-version) Choose the site and its **Version**, save changes and select **Deploy**. The `main` version publishes the live site. Other versions use `version-` Git branches and separate public test addresses under the organization’s site address base. Those versions and default-domain demos use `noindex`. Typeroll freezes the selected version, including inherited blocks, templates, collections and runtime settings, and pushes generated source to GitHub. The selected shared build engine installs the pinned dependencies and runs Astro. The resulting static files go to the Site’s Hosting Group on Cloudflare Pages. While the build or domain propagation is pending, the interface shows progress. The link appears automatically after verification. The repository is generated output. Edit through Typeroll’s UI, API or MCP. Manual repository changes are unsupported and may be replaced by a publication. ## Website and media hosts [Section titled “Website and media hosts”](https://typeroll.com/docs/guides/customer-publishing/#website-and-media-hosts) Open **Site settings → Publishing → Website and media addresses**. Set the website host and, optionally, a separate media host. For example: | Setting | Example | | ------------------------------ | ----------------------------------------------------------------------- | | Organization shared media host | `media.example.com` | | Site website host | `www.customer.com` | | Site media host | `images.customer.com` | | Media path prefix | Empty for a separate media host; `/media` when sharing the website host | Before a site has its own media host, an image can use `https://media.example.com/media//photo.png`. With a separate site media host it can use `https://images.customer.com/photo.png`. Referenced public media is included in the static site output; shared media aliases remain available through organization storage and configured URL rules. No dynamic Worker serves the website or the images. A media item’s `public_path` can preserve a legacy path such as `/wp-content/uploads/2024/photo.jpg`. Public paths are immutable: a different file must use a new path. This protects older deployments and cached links. Organization media URLs and previously published aliases remain available after a site changes its preferred host. Builds use the new preferred addresses; images are not moved into GitHub. ## Prepare a domain change before switching traffic [Section titled “Prepare a domain change before switching traffic”](https://typeroll.com/docs/guides/customer-publishing/#prepare-a-domain-change-before-switching-traffic) 1. Save the future website and media addresses. This records intent and keeps the current website address active. 2. Select **Prepare domain change from published content**. The candidate uses the last successful public snapshot, including its renderer, rather than newer saved CMS edits. Internal absolute links on the old website origin, canonicals, sitemap addresses and media references are retargeted. 3. Review **Domain verification**. Apply any validation records shown if you manage DNS externally. For Cloudflare DNS, use **your domain → DNS → Records**. Refresh verification or wait for its automatic update. 4. When the candidate and certificate checks permit it, select **Switch website traffic**. With external DNS management, apply the reviewed CNAME yourself. Version custom domains require a **proxied** Cloudflare CNAME. 5. Typeroll checks the public address before marking it live. Cloudflare Pages may require traffic DNS to validate a hostname. Typeroll does not overwrite an existing destination while its certificate is unverified. If validation cannot finish with current DNS, the change stays blocked and needs a provider-specific migration arrangement; it is not a promise of a zero-downtime cutover for every external DNS setup. Reviewed DNS changes also stop if another administrator changed the records in the meantime. ## Media migration and independent builds [Section titled “Media migration and independent builds”](https://typeroll.com/docs/guides/customer-publishing/#media-migration-and-independent-builds) After R2 and the organization shared media host are connected, Typeroll copies old originals to your private bucket, verifies SHA-256 hashes, and changes storage references only after verification. Concurrent edits and uploads granted before connection are handled without switching them to an unverified destination. Progress and retry instructions appear in Publishing. Existing source files are retained while older publications may still need them. New browser uploads go directly to your R2 account after it is ready. Disconnecting that account does not silently move uploads back to the hosting operator. The generated repository includes public content, renderer source and a dependency lock. Run `npm ci` and `npm run build` with its pinned Node version to build it on your own infrastructure. R2 originals remain separate. Cloudflare builds receive short-lived grants for the exact original and output objects. For independent builds, provide your own R2 access as described in the generated README. Forms, Apps and Extensions can still depend on their runtime services; the repository lists these in `publication.json` under `runtime_dependencies`. A generated site repository is not a full CMS backup. ## API and MCP [Section titled “API and MCP”](https://typeroll.com/docs/guides/customer-publishing/#api-and-mcp) Site API keys need admin permission for domain writes. Organization settings require an organization API key. An external agent can use its own Cloudflare access to configure DNS and R2 rules, then ask Typeroll to verify them. | Action | MCP tool | API route below `/api/v1` | | -------------------------------- | -------------------------------------- | ----------------------------------------- | | Check setup | `read_publishing_readiness` | `GET /sites/{site}/publishing` | | Read site hosts | `read_publishing_domains` | `GET /sites/{site}/publishing/domains` | | Save future hosts | `set_publishing_domains` | `PUT /sites/{site}/publishing/domains` | | Prepare last public snapshot | `prepare_publishing_domain_change` | `POST /sites/{site}/publishing/prepare` | | Approve verified traffic switch | `approve_publishing_domain_cutover` | `POST /sites/{site}/publishing/cutover` | | Read organization domain | `read_organization_publishing_domains` | `GET /publishing/domains` | | Save organization domain | `set_organization_publishing_domains` | `PUT /publishing/domains` | | Read media migration | `read_organization_media_migration` | `GET /publishing/media-migration` | | Retry media migration | `retry_organization_media_migration` | `POST /publishing/media-migration` | | Read a private image temporarily | `read_private_media_url` | `GET /sites/{site}/media/{media}/content` | | Revoke a preview | `revoke_preview_link` | `POST /sites/{site}/preview-link/revoke` | Domain writes include the current `revision`. Cutover also includes `candidate_id`. A stale revision returns 409. Use the normal deploy-status API to inspect build progress and `read_publishing_domains` for structured DNS requirements. --- # Migrate from Squarespace Source: [Migrate from Squarespace](https://typeroll.com/docs/guides/migrate-from-squarespace/) > Move accessible Squarespace content into Typeroll CMS with an AI agent, then verify images, links and redirects before publishing. Use the [Import from URL recipe](https://typeroll.com/docs/skills/tr-import-url/) with your connected AI agent. This is a content migration workflow, not a Squarespace account connector or an automatic conversion of a Squarespace template. ## What can move [Section titled “What can move”](https://typeroll.com/docs/guides/migrate-from-squarespace/#what-can-move) The agent can read accessible public pages, extract text and semantic HTML, transfer reachable images through Typeroll’s media API, and create pages or collection content. Preserve existing paths where possible and record redirects for paths that change. Layouts, galleries and repeated sections need to be rebuilt using the target site’s blocks, templates and styles. Forms, commerce, member areas, scheduling, analytics scripts and other hosted behavior need separate replacements or integrations. They are not transferred by copying page HTML. ## Before you start [Section titled “Before you start”](https://typeroll.com/docs/guides/migrate-from-squarespace/#before-you-start) Create the target Site and set up its design, header and footer. Give your agent an API or MCP connection, the source address and the pages that matter. Keep the Squarespace site available until content and media have been verified. Squarespace’s XML export covers selected content and is primarily intended for WordPress. It is not a complete site backup or a dedicated Typeroll import format. See [Squarespace’s export guide](https://support.squarespace.com/hc/en-us/articles/206566687-Exporting-your-site) for its current exclusions. ## Migration steps [Section titled “Migration steps”](https://typeroll.com/docs/guides/migrate-from-squarespace/#migration-steps) 1. Ask the agent to inventory the homepage, sitemap and internal links. Compare the result with your own page list, including blog posts and image-heavy pages. 2. Import one representative page as a draft. Have the agent extract content, transfer the images and adapt it to the target design. 3. Review it in the [visual editor](https://typeroll.com/docs/guides/the-editor/). Imported HTML is not automatically a fully editable block tree; request blocks for sections your editors need to maintain visually. 4. Import the remaining pages. Rebuild galleries, Forms and navigation, and record redirects for changed URLs. 5. Review text, headings, image alt text, canonicals and mobile layout. Ask for a report of inaccessible or skipped URLs rather than assuming full coverage. 6. Save approved content and complete [publishing setup](https://typeroll.com/docs/guides/customer-publishing/). Verify a test deployment before [moving the live domain](https://typeroll.com/docs/publishing/domains/). ```text Import the public content from https://source.example into this Typeroll Site. Use our existing design. Start with one gallery page as a draft, report anything unavailable, and keep a list of old and new URLs. Do not publish yet. ``` If a page blocks fetching or needs authentication, supply the text, images or source files you can legitimately access. Do not treat missing HTML as an empty page. --- # Migrate from Webflow Source: [Migrate from Webflow](https://typeroll.com/docs/guides/migrate-from-webflow/) > Import Webflow page content or export files into Typeroll CMS with an AI agent, and map collections, images and old URLs explicitly. Use an [AI agent](https://typeroll.com/docs/getting-started/mcp-server/) and the [Import from URL recipe](https://typeroll.com/docs/skills/tr-import-url/) to adapt accessible Webflow content to a Typeroll Site. There is no dedicated Webflow account connector. ## Choose your source [Section titled “Choose your source”](https://typeroll.com/docs/guides/migrate-from-webflow/#choose-your-source) Publicly accessible HTML can supply rendered page content. If your Webflow plan provides code export, those files can also serve as input for the agent. Webflow’s code export does not include the CMS and ecommerce databases or all hosted functionality. Collection CSV exports are separate; plan their field mapping explicitly. See [Webflow’s code export documentation](https://help.webflow.com/hc/en-us/articles/33961386739347-How-do-I-export-my-Webflow-site-code). ## What can move [Section titled “What can move”](https://typeroll.com/docs/guides/migrate-from-webflow/#what-can-move) The agent can extract page text, semantic structure and reachable images. For collection data, define the target Typeroll schema, map exported fields and references, then create items through the collection tools. Webflow classes, layout rules and interactions are not a guaranteed editable Typeroll block structure. Rebuild shared headers, footers and page templates in Typeroll. Forms, ecommerce, account features, search and external scripts require separate decisions and testing. ## Migration steps [Section titled “Migration steps”](https://typeroll.com/docs/guides/migrate-from-webflow/#migration-steps) 1. Create the target Site, design settings and shared layout. Inventory static pages and collection detail pages as separate groups. 2. Provide public URLs or source files to the agent. If using CSV, agree the collection schema and URL mapping before importing records. 3. Import one static page and one collection item as drafts. Transfer media and replace source-specific styling with the target design. 4. Review the result in the [editor](https://typeroll.com/docs/guides/the-editor/). Request blocks for sections that clients need to edit; HTML import alone does not create them. 5. Import remaining content, check relationships, rebuild dynamic functions and create redirects for changed paths. Report missing records and media. 6. Save approved content and verify a deployment with your [publishing accounts](https://typeroll.com/docs/guides/customer-publishing/) before preparing the [production-domain switch](https://typeroll.com/docs/publishing/domains/). ```text Import these Webflow pages and the supplied collection CSV into this Typeroll Site. Propose the collection schema and URL mapping first. Rebuild shared layouts as Typeroll templates, preserve image alt text, and keep the content as drafts. ``` Verify forms, filtering, navigation, media paths and metadata independently. A successful content import does not prove the old site’s interactive features have been recreated. --- # Migrate from Wix Source: [Migrate from Wix](https://typeroll.com/docs/guides/migrate-from-wix/) > Rebuild Wix content in Typeroll CMS with your AI agent, preserving URLs and images while replacing Wix-specific functionality. A Wix migration recreates your content in Typeroll CMS. It does not move the Wix application itself. Wix documents that its site technology depends on Wix hosting; do not plan around a complete HTML export that can run unchanged elsewhere. See [Wix’s hosting and export explanation](https://support.wix.com/en/article/exporting-or-embedding-your-wix-site-elsewhere). ## What can move [Section titled “What can move”](https://typeroll.com/docs/guides/migrate-from-wix/#what-can-move) Use public page content that your agent can retrieve, or provide text, original images and authorized source material yourself. The agent can create Typeroll pages and collections through the API or MCP and upload the required media. Wix layouts, animations, forms, bookings, stores, memberships and app behavior do not become Typeroll features just by copying visible content. Plan replacements for the functionality you still need. Screenshots can guide layout, but they do not contain editable text, original images or application data. ## Before you start [Section titled “Before you start”](https://typeroll.com/docs/guides/migrate-from-wix/#before-you-start) Create a target Site and define its design. List the current public paths and collect source image files. Identify business functions such as forms and bookings separately from page content. Keep Wix live during review. A page may rely on JavaScript or block automated fetching. Have your agent check what it actually receives; do not assume every Wix page is either fully readable or an empty shell. ## Migration steps [Section titled “Migration steps”](https://typeroll.com/docs/guides/migrate-from-wix/#migration-steps) 1. Connect your [AI agent](https://typeroll.com/docs/getting-started/mcp-server/) and provide the source address, page list and any text or image files needed to fill gaps. 2. Ask it to use the [Import from URL recipe](https://typeroll.com/docs/skills/tr-import-url/) and report inaccessible pages before creating replacements. 3. Rebuild a representative page as a draft using blocks where you want visual editing. Review it on desktop and mobile before importing the rest. 4. Transfer images through Typeroll media tools. Recreate Forms and other integrations separately, including their success and error states. 5. Keep paths where practical and create redirects for changed URLs. Check each source URL against the [migration inventory](https://typeroll.com/docs/tools/migration-urls/). 6. Save reviewed content, [set up publishing](https://typeroll.com/docs/guides/customer-publishing/) and test the site before changing the [live domain](https://typeroll.com/docs/publishing/domains/). ```text Rebuild these Wix pages in this Typeroll Site using editable blocks. Use the supplied text and original images where public fetching is incomplete. List functionality that needs a replacement. Keep everything as drafts. ``` Typeroll does not currently provide a dedicated one-click Wix account importer. The amount of manual preparation depends on the source content and integrations. --- # Self-Hosting Source: [Self-Hosting](https://typeroll.com/docs/guides/self-hosting/) > Run Typeroll on your own infrastructure — requirements, environment variables, and deployment. Typeroll is open-source (MIT) and can be self-hosted. The supported reference profile runs entirely in a GCP/Firebase project you own. Cloud Run hosts the portal and Forms from the same immutable Core image used by Typeroll Cloud; Cloud Tasks and Cloud Scheduler invoke authenticated internal routes when work is ready. There is no VM or always-running worker. ## Architecture [Section titled “Architecture”](https://typeroll.com/docs/guides/self-hosting/#architecture) ```text Internet ── Cloud Run portal ───── Firebase Auth + Firestore │ │ ├── Cloud Tasks ──────────┤ deploy-worker route ├── Cloud Scheduler ──────┤ publish-sweep route ├── R2 media └── Cloudflare Pages static sites Internet ── Cloud Run Forms ────── Firebase Auth + Firestore ``` Both Cloud Run services use the same verified Core release. The public image is copied by digest into the customer’s Artifact Registry without rebuilding it. The portal enqueues durable Cloud Tasks; each task invokes `/api/internal/deploy-worker` with an OIDC token. Cloud Scheduler invokes `/api/internal/publish-sweep` with the same identity contract. The runtime uses Application Default Credentials from dedicated Cloud Run service accounts. Do not create or mount a Firebase service-account key for the reference profile. ## Prerequisites [Section titled “Prerequisites”](https://typeroll.com/docs/guides/self-hosting/#prerequisites) * **Development:** Node.js 22.12 or later * **Production:** a GCP project with billing, `gcloud`, and `crane` * **Firebase:** Firestore Native mode, Authentication, and a Firebase Web App * **Cloudflare account** with R2 for media and Pages for static site output * Two public DNS names for the portal and Forms origins * **Optional:** Anthropic API key for AI chat ## Quick start (local dev) [Section titled “Quick start (local dev)”](https://typeroll.com/docs/guides/self-hosting/#quick-start-local-dev) Clone the repository and install dependencies: ```bash git clone https://github.com/typeroll/typeroll cd typeroll npm ci ``` The portal includes a **fixtures backend** — a JSON file store that replaces Firestore for local development. No Firebase configuration needed to run locally: ```bash npm run dev:portal ``` Open `http://localhost:4321`. You’ll be logged in as a dev user with access to the sample content in `packages/portal/fixtures/`. ## Plan the reference profile [Section titled “Plan the reference profile”](https://typeroll.com/docs/guides/self-hosting/#plan-the-reference-profile) Clone a tagged Core release and copy the non-secret serverless configuration contract: ```bash git clone --branch core-v --depth 1 https://github.com/typeroll/typeroll cd typeroll cp config/self-host-gcp.example.json self-host.gcp.json ``` Fill in the customer project, region, Firebase Web App, public origins, resource names, Secret Manager resource names, and the release’s published `@sha256:` image. The JSON contains references to secrets, never secret values. Generate an inspectable, non-mutating plan: ```bash npm ci npm run self-host:gcp:plan -- --config self-host.gcp.json > self-host.gcp.plan.json ``` The plan fixes the supported topology before any remote mutation: required APIs, three least-privilege identities, Artifact Registry mirror, Cloud Run portal and Forms services, Cloud Tasks queue, Cloud Scheduler job, runtime configuration, and Secret Manager bindings. It also records that the artifact is copied by digest without a rebuild and explicitly forbids VM, managed instance group, reverse proxy, and long-running-worker resources. Every required Secret Manager resource must have an enabled `latest` version before deployment. Keep the actual secret values in your secret-management workflow; do not add them to `self-host.gcp.json` or Git. ## Apply and verify the reference profile [Section titled “Apply and verify the reference profile”](https://typeroll.com/docs/guides/self-hosting/#apply-and-verify-the-reference-profile) The apply command is a dry run unless `--apply` and an exact project confirmation are both present. Its two rerunnable phases separate resources that contain no secret values from the runtime deployment: ```bash # Local preview only; makes no remote calls. npm run self-host:gcp:apply -- --config self-host.gcp.json # APIs, service accounts, IAM, Artifact Registry, Cloud Tasks, and empty # Secret Manager containers. This changes the named GCP project. npm run self-host:gcp:apply -- \ --config self-host.gcp.json \ --phase foundation \ --apply \ --confirm-project your-gcp-project ``` Add one enabled version to every Secret Manager resource using your normal secret-management workflow. The apply command creates secret containers and IAM bindings, but it never accepts, reads, prints, or copies secret values. Then preview and apply the runtime phase: ```bash npm run self-host:gcp:apply -- \ --config self-host.gcp.json \ --phase runtime npm run self-host:gcp:apply -- \ --config self-host.gcp.json \ --phase runtime \ --apply \ --confirm-project your-gcp-project ``` The runtime phase fails before copying an image or deploying Cloud Run if any secret lacks an enabled version. It verifies the source digest, copies the release to Artifact Registry under a deterministic digest tag without a rebuild, deploys the portal and Forms roles, and converges the publish-sweep Scheduler job. A new portal receives one bootstrap revision so its stable `run.app` URL can be discovered; the command immediately converges the active revision and both OIDC callers on that URL. Run the read-only control-plane doctor at any time. It reads resource and secret-version metadata, never secret contents: ```bash npm run self-host:gcp:doctor -- --config self-host.gcp.json npm run self-host:gcp:doctor -- --config self-host.gcp.json --json ``` The operator running apply must already be allowed to enable services, manage the listed project resources and IAM bindings, and act as the three dedicated runtime service accounts. Core does not grant roles to the human or automation identity running the installer. After deployment, verify both roles and the immutable release identity: ```bash curl --fail https://cms.example.com/api/healthz curl --fail https://cms.example.com/api/readyz curl --fail https://cms.example.com/api/version curl --fail https://forms.example.com/api/readyz curl --fail https://forms.example.com/api/version ``` ## Portable Compose fallback [Section titled “Portable Compose fallback”](https://typeroll.com/docs/guides/self-hosting/#portable-compose-fallback) `compose.yaml` is retained for local evaluation and deployments outside GCP. It is not the production-supported profile and is not the permanent self-host E2E target. For this fallback only, copy the environment contract and keep it readable only by the operator: ```bash cp .env.self-host.example .env chmod 600 .env ``` `TYPEROLL_IMAGE` must use the release’s published `@sha256:` digest, not a mutable tag. Set `TYPEROLL_IMAGE_DIGEST` to the same digest. Generate each HMAC/encryption value independently: ```bash node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))" ``` Generate the Extension issuer key once and store the resulting private JWK as single-line JSON in `EXTENSION_SIGNING_PRIVATE_JWK`: ```bash node --input-type=module -e "import { generateKeyPairSync } from 'node:crypto'; const { privateKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' }); console.log(JSON.stringify(privateKey.export({ format: 'jwk' })))" ``` Validate the complete portable contract before Docker receives credentials: ```bash npm ci npm run self-host:check docker compose config --quiet ``` All self-host operation commands also accept the complete contract through their process environment. Process-injected values override an `--env-file`, and a file is optional when the injected environment is complete. Prefer that mode with a secret-manager runner so JSON keys and recovery credentials do not need to be materialized on disk. The `0600` check still applies whenever an environment file is supplied. The check prints field names and validation failures only. It never prints secret values. Keep `TYPEROLL_BACKUP_KEY` in a second, off-host secret store; losing both the host and this key makes encrypted backups unrecoverable. The Compose profile explicitly removes this key from every Core container. Bootstrap the portable data contract once before starting Core: ```bash npm run self-host:bootstrap ``` Bootstrap is idempotent. It refuses a project or bucket containing data unless you pass `--adopt`; use that flag only after verifying all existing resources belong to this Typeroll installation. Start the installation when the checks and bootstrap pass: ```bash docker compose pull docker compose up -d docker compose ps ``` Caddy requests TLS certificates after both DNS names point at the host and ports 80 and 443 are reachable. Verify every portable role: ```bash curl --fail https://cms.example.com/api/healthz curl --fail https://cms.example.com/api/readyz curl --fail https://cms.example.com/api/version curl --fail https://forms.example.com/api/readyz docker compose exec worker node -e "fetch('http://127.0.0.1:8080/api/readyz').then(async r => { console.log(await r.text()); process.exit(r.ok ? 0 : 1) })" ``` The Forms hostname exposes only Forms, Analytics, and diagnostic routes. The portable worker is not published through Caddy. Its Firestore queue is durable; do not replace `DEPLOY_QUEUE=firestore` with `in_process` in an internet-facing portable install. ## Build-cost accounting [Section titled “Build-cost accounting”](https://typeroll.com/docs/guides/self-hosting/#build-cost-accounting) Every deploy records what the build cost in server time, on the deploy job itself. You’ll see it in `get_deploy_status` responses — total, a CPU/memory/request split, wall-clock duration, per-phase timings, and the size of the generated site. Nothing needs configuring to get this. The defaults above are the published rates for a request-billed container at 1 vCPU / 1 GiB, which is how the reference deployment runs. Adjust the rates when your hosting differs: * **Different provider or region.** Substitute your own per-second rates. * **Committed-use or reserved pricing.** Use your effective discounted rates. * **Your own hardware.** Set the rates to `0` — every build then costs nothing, which is accurate: you’ve already paid for the machine. Two things to keep in mind: * **`DEPLOY_COST_VCPU` and `DEPLOY_COST_MEMORY_GIB` must match what you actually allocate** to the worker container. Change your container’s CPU or memory without changing these and every subsequent build is mispriced. * **The rate card is snapshotted onto each deploy job when it runs.** Changing a rate affects future builds only; history keeps the numbers it was costed with, so a rate change never silently rewrites past figures. The numbers are estimates from that rate card, not billing records, and they’re gross — free-tier allowances and discounts aren’t deducted, so your real invoice will be lower. ## Firebase setup [Section titled “Firebase setup”](https://typeroll.com/docs/guides/self-hosting/#firebase-setup) 1. Create a Firebase project at [console.firebase.google.com](https://console.firebase.google.com/). 2. Enable **Firestore** in Native mode and **Authentication → Email/Password**. 3. Add a Firebase web app and copy its four public values to the serverless config. 4. Attach the dedicated runtime service accounts from the generated plan to the Cloud Run services. Core uses `FIREBASE_PROJECT_ID` and Application Default Credentials; do not create a downloadable key. 5. Ensure the backup identity can read the Authentication password hash configuration (`firebaseauth.configs.getHashConfig`) in addition to managing Auth users and Typeroll’s Firestore data. Backups fail closed if password hashes or their hash configuration are unavailable. 6. For each user, set an `org_id` custom claim with the modular Admin API: ```javascript import { applicationDefault, initializeApp } from "firebase-admin/app"; import { getAuth } from "firebase-admin/auth"; initializeApp({ credential: applicationDefault(), projectId: process.env.FIREBASE_PROJECT_ID, }); await getAuth().setCustomUserClaims(uid, { org_id: "your-org-id" }); ``` All content is scoped under `organizations/{org_id}/` in Firestore. The portable Compose fallback instead accepts `FIREBASE_SERVICE_ACCOUNT` JSON because it does not have a GCP workload identity. ## Backups and disaster recovery [Section titled “Backups and disaster recovery”](https://typeroll.com/docs/guides/self-hosting/#backups-and-disaster-recovery) Typeroll’s backup is one encrypted, authenticated snapshot of all three state planes: * Firestore documents, subcollections, native value types, and installation schema metadata * Firebase Authentication users, password hashes, password hash configuration, provider identities, custom claims, and MFA metadata * R2 objects and their relevant HTTP metadata The manifest exposes only source identifiers, schema version, timestamps, and counts. Data, user records, object keys, and object bodies are encrypted with AES-256-GCM. Every file and the manifest are authenticated before restore. For a consistent manual backup of the portable Compose fallback, stop writers but leave its managed dependencies online: ```bash docker compose stop portal forms worker npm run self-host:backup -- --output /srv/typeroll-backups/2026-09-01 npm run self-host:restore -- --backup /srv/typeroll-backups/2026-09-01 docker compose start portal forms worker ``` The restore command is verify-only unless `--apply` is present. Copy the completed backup directory off-host and test that copy with the verify-only command. Never copy a directory containing `INCOMPLETE`. Restore to a completely empty Firebase project and R2 bucket with exact target and backup confirmations: ```bash npm run self-host:restore -- \ --backup /srv/typeroll-backups/2026-09-01 \ --apply --empty-target \ --confirm-project my-disaster-recovery-project \ --confirm-bucket my-disaster-recovery-bucket \ --confirm-backup \ --allow-target-mismatch ``` `--allow-target-mismatch` is required only when the backup source identifiers differ from an intentional disaster-recovery target. The restore writes the target identifiers into the recovered installation metadata. To replace an existing installation, stop every Core role and substitute `--replace` for `--empty-target`. Replace mode deletes Firestore documents, Auth users, and R2 objects that are absent from the backup. It verifies the entire backup first, requires all exact confirmations, and verifies the final record sets, but it cannot make three external services transactional. Keep the roles stopped until the command succeeds; if it fails, correct the cause and rerun the same idempotent restore. At least quarterly, restore the latest backup into isolated Firebase and R2 test resources, start the pinned Core image against them, and run the browser and agent acceptance suite. A backup that has only been created is not yet a proven recovery path. ## Portable upgrade, migration, and rollback [Section titled “Portable upgrade, migration, and rollback”](https://typeroll.com/docs/guides/self-hosting/#portable-upgrade-migration-and-rollback) Core reports both its release and readable data-schema range at `/api/version`. The command-level sequence below applies to the portable Compose fallback. The serverless profile instead promotes a new digest through the generated GCP plan and keeps the previous Cloud Run revision available for traffic rollback. 1. Stop portal, Forms, and worker so the backup and migration have no writers. 2. Create an encrypted backup and verify the copied backup directory. 3. Check out the new signed/tagged Core release, run `npm ci`, `npm run self-host:check`, and `docker compose config --quiet`. 4. Inspect the migration without changing data: ```bash npm run self-host:migrate ``` 5. If migrations are pending, apply them only with the verified pre-migration backup and exact project confirmation: ```bash npm run self-host:migrate -- \ --apply \ --backup /srv/typeroll-backups/2026-09-01 \ --confirm-project my-firebase-project ``` 6. Set `TYPEROLL_IMAGE` and `TYPEROLL_IMAGE_DIGEST` to the new published digest, then run `docker compose pull`, `docker compose up -d`, and the health, readiness, and version checks above. 7. Run a site edit/deploy smoke test, one Forms submission, the browser visual suite, and the agent suite before ending the maintenance window. Migration steps are ordered, idempotent, protected by a renewable installation lock, and update schema metadata after each successful step. They will not run without a matching verified backup. If the new image fails while the previous image can read the resulting schema, pin the previous digest again and restart Compose. If the previous image cannot read the new schema, leave all roles stopped and restore the pre-migration backup with `--replace`, then start the previous digest. Never run an image against a schema outside the readable range reported by that image. ## Direct Docker smoke test [Section titled “Direct Docker smoke test”](https://typeroll.com/docs/guides/self-hosting/#direct-docker-smoke-test) For image development only, the container listens on port 8080: ```bash docker build -t typeroll-core:dev . docker run --rm -p 8080:8080 --env-file .env typeroll-core:dev ``` This single-container command does not provide the separate Forms origin or durable jobs and is not the production installation profile. ## Hosted MCP connector [Section titled “Hosted MCP connector”](https://typeroll.com/docs/guides/self-hosting/#hosted-mcp-connector) A self-hosted portal serves the MCP Streamable HTTP transport at `/api/mcp` automatically — no extra deployment step. Your users add `https:///api/mcp` as a remote server in their MCP-compatible client, follow the consent screen, and enter a Typeroll API key (org-scoped from `/app/settings/api-keys` is the right default). Requirements specific to this endpoint: * `MCP_OAUTH_SIGNING_KEY` set (see above). Without it the `/api/mcp/oauth/token` endpoint refuses to issue JWTs. * `PORTAL_PUBLIC_URL` set to the public origin (`https://`). The OAuth `aud` claim and the well-known metadata endpoints use this to bind issued tokens to your domain per RFC 8707. * TLS for remotely hosted MCP clients. Agent clients with local process support can keep using the stdio install (`npx -y @typeroll/mcp-server` with `TYPEROLL_API_URL` pointed at your portal). Same tool surface, same API keys. ## Extensions [Section titled “Extensions”](https://typeroll.com/docs/guides/self-hosting/#extensions) A self-hosted portal can register and install private or unlisted Extensions without contacting Typeroll’s hosted catalog. Set `EXTENSION_SIGNING_PRIVATE_JWK` to a P-256 private JWK and keep it outside source control. Native Extension form bindings also require `FORMS_HMAC_SECRET`. Set `FORMS_PUBLIC_URL` when form submissions are served from a dedicated service; otherwise components call the self-hosted `PORTAL_PUBLIC_URL` endpoint directly. Customer site deployment remains static and contains no generated Functions. The portal publishes issuer discovery and JWKS from its own `PORTAL_PUBLIC_URL`. Extension providers must pair and validate that issuer instead of assuming the hosted Typeroll domain. During key rotation, set `EXTENSION_SIGNING_PREVIOUS_PUBLIC_JWKS` to the old public JWKS for an overlap window, issue all new tokens with the new private key, then remove the old keys after the longest token and pairing lifetime has passed. See [Extensions](https://typeroll.com/docs/extensions/overview/) for the manifest, runtime, provider, and recipient-link contracts. The Extension protocol does not bundle premium Typeroll Apps. Those apps are sold and operated separately in Typeroll-controlled accounts even when the CMS is self-hosted. Third-party and bespoke backends likewise remain in their developer’s accounts. In both cases the browser calls the provider directly; the self-hosted portal issues identity tokens but never proxies provider API traffic. Other platforms can run the same three image roles, but the Compose profile is the compatibility baseline. Preserve the separate origins, immutable digest, and durable queue contract when translating it to another orchestrator. --- # The Editor Source: [The Editor](https://typeroll.com/docs/guides/the-editor/) > The visual block editor — adding blocks, editing on the canvas, responsive breakpoints, undo/redo, and getting work live. Use the visual editor to create pages, maintain content and review changes made by an AI agent. Browser editing does not require an AI connection or publishing accounts. Choose the **Organization**, select the **Site** and **Version**, then open **Pages** and select a page. On a phone, open the sidebar menu first. ## Desktop layout [Section titled “Desktop layout”](https://typeroll.com/docs/guides/the-editor/#desktop-layout) ![The real Typeroll CMS block editor with sample content: structure on the left, preview in the middle and heading fields on the right.](https://typeroll.com/docs/images/editor-desktop.png) The editor displays three panels when the screen is wide enough: | Panel | What it holds | | ------ | -------------------------------------------------------------------------- | | Left | **Add** for the block library and **Structure** for the page’s block tree. | | Center | A live preview rendered from the page content. | | Right | Fields for the selected block, or page metadata when no block is selected. | The header provides undo/redo, preview devices, **Meta** for page settings and **Publish** for saving, status and deployment. ## On a phone [Section titled “On a phone”](https://typeroll.com/docs/guides/the-editor/#on-a-phone) Narrow screens show one panel at a time. Use **Blocks**, **Preview** and **Edit block** or **Settings** to switch panels. Select a block from **Structure** to open its fields. **Publish** stays in the header, including the deploy control when publishing setup and your permissions allow it. ![Mobile editor showing the selected heading's editable fields and the Blocks, Preview and Edit block panel controls.](https://typeroll.com/docs/images/editor-mobile.png) The mobile layout changes how you navigate the editor. The preview device selector separately controls which responsive breakpoint you are editing. ## Adding blocks [Section titled “Adding blocks”](https://typeroll.com/docs/guides/the-editor/#adding-blocks) Open the **Add** tab and either: * **Click** a block — it’s appended to the end of the page, or * **Drag** it into the **Structure** tree — a coloured drop-line follows your pointer showing exactly where it will land, including inside containers Dragging is how you place a block precisely. Clicking is faster when you’re just appending. Search the library by name at the top of the tab. Block ids are per-site — your custom blocks appear in the same list as the built-in ones. ## Editing content [Section titled “Editing content”](https://typeroll.com/docs/guides/the-editor/#editing-content) Three ways, depending on what you’re changing: **Click a block** in the preview or the tree to select it. Its fields appear in the right column, generated from the block’s own schema. **Double-click text directly on the canvas** to edit it in place. Enter or clicking away commits; Escape cancels. This works on plain-text fields — headings, labels, button text — and is the fastest way to fix a typo. **Use the Structure tree** for anything about arrangement rather than content: reorder by dragging, duplicate, delete, or double-click a row to rename the block (the name is a label for you; it doesn’t appear on the site). The tree shows each block’s actual content — the heading text, the image filename — rather than just its type, so a long page stays navigable. ## Keyboard shortcuts [Section titled “Keyboard shortcuts”](https://typeroll.com/docs/guides/the-editor/#keyboard-shortcuts) | Shortcut | Does | | ---------------------- | ------------------------------------- | | `⌘Z` / `Ctrl+Z` | Undo | | `⇧⌘Z` / `Ctrl+Shift+Z` | Redo | | `⌘D` / `Ctrl+D` | Duplicate the selected block | | `Delete` / `Backspace` | Delete the selected block | | `Enter` | Commit an inline edit or a rename | | `Escape` | Cancel an inline edit, close a dialog | Undo covers block edits — adding, moving, deleting, field changes. A burst of typing in one field collapses into a single undo step, so one `⌘Z` takes you back to before you started typing, not one character at a time. ## Responsive editing [Section titled “Responsive editing”](https://typeroll.com/docs/guides/the-editor/#responsive-editing) The device buttons in the header do **two** things at once, and the second one is easy to miss: 1. They resize the preview. 2. They set **which breakpoint you’re editing**. So if you pick **Phone** and then change a grid’s column count, you’ve set that value *for phones only*. Switch back to **Desktop** and the desktop value is untouched. | Button | Preview width | Breakpoint written | | ------------------ | ------------- | ------------------ | | Phone | 390px | `mobile` (< 640) | | Phone — landscape | 740px | `tablet` (≥ 640) | | Tablet | 1024px | `laptop` (≥ 1024) | | Tablet — landscape | 1280px | `desktop` (≥ 1280) | | Desktop | full width | `wide` (≥ 1536) | Fields that support per-breakpoint values show a small badge with the current breakpoint. When a value is inherited rather than set explicitly, the badge says **inherited** and offers a reset control to drop back to the inherited value. Breakpoints inherit upward from mobile: set something once at Phone and it applies everywhere unless a larger breakpoint overrides it. That’s why it’s usually best to design mobile-first and only override where a larger screen genuinely needs something different. Check which device is active before editing A responsive change lands on whichever device button is highlighted. If a change seems not to apply, you’re probably looking at a different breakpoint than the one you edited. ## Page metadata [Section titled “Page metadata”](https://typeroll.com/docs/guides/the-editor/#page-metadata) Click **Meta** in the header, or just deselect the current block, to get the page’s own fields: page title, SEO title, meta description, OG image and alt text, canonical URL, and the noindex toggle. The same panel holds the **Template** picker and the **Content mode** switch (blocks ↔ HTML). ## Saving and publishing [Section titled “Saving and publishing”](https://typeroll.com/docs/guides/the-editor/#saving-and-publishing) Content edits are kept in a **working copy**. They do not change the saved publication input or the live site until you save and deploy. Read the save state in the header, then open **Publish**: 1. **Changes** — Save (commits the draft) or Discard (throws it away). 2. **Status** — Draft, In review, Unlisted, or Published. Status changes apply to the saved page. They control inclusion in the next deployment; changing status does not by itself deploy the site. 3. **Deploy** — rebuilds the static site from saved, published content. The panel summarizes pending changes below the deploy control. Open the change list for details. Build and distribution progress appear here; a public link is shown only after the deployment has been verified. The Pages list also provides a site publishing control. If deployment is locked, follow the link to [Publishing setup](https://typeroll.com/docs/guides/customer-publishing/). You can keep editing and use expiring previews while setup is incomplete. ### Reviewing before you save [Section titled “Reviewing before you save”](https://typeroll.com/docs/guides/the-editor/#reviewing-before-you-save) If the AI agent built something substantial, use **Review changes**. It shows the saved page and your draft side by side in the same renderer, plus a structured list of what changed block by block. That’s the intended way to audit an agent’s work: look at the diff, then save. ## HTML-mode pages [Section titled “HTML-mode pages”](https://typeroll.com/docs/guides/the-editor/#html-mode-pages) Pages in HTML mode get a different editor — a CodeMirror pane with syntax highlighting instead of the block library, plus the same preview, metadata and Publish menu. You also get **SERP** and **Open Graph** previews showing how the page will look in Google results and when shared on social, and a **History** panel for restoring earlier revisions. Switch a page between modes with the **Content mode** control. Converting HTML to blocks is heuristic — check the result. Converting blocks to HTML discards the block tree. Both directions save a revision first, so neither is a one-way door. ## Templates and custom blocks [Section titled “Templates and custom blocks”](https://typeroll.com/docs/guides/the-editor/#templates-and-custom-blocks) Two related editors, reachable from the site sidebar: * **Templates** — a block tree that wraps a page’s content. Build the shared outer structure once, drop a **content slot** where the page’s own blocks should render, then assign the template to pages. * **Blocks** — create reusable custom block types for this site: schema, HTML template, scoped CSS, and optionally client-side JavaScript (which requires an explicit opt-in, because it runs in your visitors’ browsers). --- # WordPress Migration Source: [WordPress Migration](https://typeroll.com/docs/guides/wordpress-migration/) > A complete guide to migrating a WordPress site to Typeroll using the AI migration workflow. The migration workflow and helper plugin are included in the open-source Typeroll distribution. Typeroll Cloud also offers migration as an operated, supported service. ## Overview [Section titled “Overview”](https://typeroll.com/docs/guides/wordpress-migration/#overview) The migration workflow imports accessible WordPress content (pages, posts and supported custom post types) into a new Typeroll site, transfers your media through Typeroll’s media storage, and generates redirects from old URLs to new slugs. **What it does:** * Imports pages and posts as Typeroll pages * Transfers media (images) through the media API — only images actually used on imported pages * Maps custom post types to Typeroll collections * Generates URL redirects * Leaves everything as drafts for your review before going live **What it doesn’t do:** * Import your old WordPress theme or CSS (you build a new design in Typeroll) * Import plugins or plugin-specific data that isn’t content * Touch the source WordPress site in any way ## Before you start [Section titled “Before you start”](https://typeroll.com/docs/guides/wordpress-migration/#before-you-start) 1. **Build the target site’s design** The migration imports content into your new design — not the old one. Before starting the migration, set up: * Colour palette and fonts (`update_site_settings`) * Header partial with navigation * Footer partial * 1–2 example pages (home, about) — the AI agent uses these as design references 2. **Check that the WP REST API is accessible** The migration needs `https://yoursite.com/wp-json/wp/v2/` to be publicly accessible. Test it in your browser — you should see a JSON response. If the REST API is disabled or restricted, [install the helper plugin](https://typeroll.com/docs/guides/wordpress-migration/#helper-plugin) which provides a separate authenticated export endpoint. Your administrator must allow access to that endpoint. 3. **Optional: install the helper plugin** The helper plugin gives the AI agent access to custom post types, ACF fields and navigation menus that may not be exposed via the standard REST API. ## Running the migration [Section titled “Running the migration”](https://typeroll.com/docs/guides/wordpress-migration/#running-the-migration) Start your AI agent and describe the migration: ```plaintext Migrate my WordPress site at https://acme.se to this Typeroll site. ``` Or with the helper plugin: ```plaintext Migrate the WordPress site at https://acme.se — I've installed the helper plugin and configured its API key securely in the agent environment. ``` The AI agent will: 1. Verify the WP REST API connection 2. Build an inventory of all URLs on the site 3. Import pages and posts, transferring images along the way 4. Map any custom post types to collections 5. Generate redirects 6. Show you a coverage report ## Reviewing the migration [Section titled “Reviewing the migration”](https://typeroll.com/docs/guides/wordpress-migration/#reviewing-the-migration) After the content is imported, the AI agent shows a coverage report: ```plaintext Migration complete. Here's the coverage: ✅ 24 pages migrated ↪ 8 redirects created ⬜ 12 URLs excluded (pagination, feeds, admin) ⚠️ 3 URLs unhandled: /category/projekt → needs a redirect or category page /tag/stockholm → tag archive — exclude or redirect to /blog /wp-content/uploads/2019/hero.pdf → file, needs manual handling ``` For each unhandled URL, decide: add a redirect, create a page, or exclude it. Sites imported with an older Typeroll version may have WordPress entities or markup left in titles and SEO text. Ask the AI agent to run `repair_migration_plain_text` as a dry run and show every proposed change. The repair is restricted to plain-text fields and leaves bodies, slugs, paths, and URLs alone. Approve the write only after reviewing the returned diffs; records with existing working copies are skipped rather than overwritten. ## Publishing [Section titled “Publishing”](https://typeroll.com/docs/guides/wordpress-migration/#publishing) All imported pages start as `status: "review"`. When you’re satisfied: ```plaintext Publish all the imported pages and deploy. ``` Or review page by page and publish individually. ## Helper plugin [Section titled “Helper plugin”](https://typeroll.com/docs/guides/wordpress-migration/#helper-plugin) The Typeroll Helper Plugin gives the migration workflow access to: * Custom post types with `show_in_rest: false` * ACF fields with image references normalised * Navigation menus * Builder JSON (Elementor, Breakdance) * Complete canonical URL list **Installing:** 1. Download the `wp-helper-plugin` directory from the Typeroll repository and create a ZIP archive from it. 2. In WordPress: **Plugins → Add New → Upload Plugin** 3. Activate the plugin 4. Go to **Settings → Typeroll Helper** and generate an API key 5. Give the agent the site URL and configure the key through its secure credential mechanism. Keep the key out of shared prompts and documentation. --- # Cloudflare hosting and builds Source: [Cloudflare hosting and builds](https://typeroll.com/docs/publishing/cloudflare/) > Understand how Typeroll CMS separates GitHub source, shared build execution, Cloudflare Pages hosting and R2 media. Typeroll publishes static files to Cloudflare Pages. Source storage, building and hosting are separate choices: GitHub stores generated source, the organization’s selected build provider runs the build, and each Site’s Hosting Group receives the finished files. ## What each service does [Section titled “What each service does”](https://typeroll.com/docs/publishing/cloudflare/#what-each-service-does) | Service | Role in customer publishing | | ---------------------------- | -------------------------------------------------------------------------------------------- | | GitHub | A generated private repository for each Site, with version branches. | | Cloudflare Workers Builds | One shared build engine for the Organization, when Cloudflare is selected as build provider. | | GitHub Actions | The alternative shared build engine, selected for the Organization. | | Cloudflare Pages | Static website and referenced-media output, in the Site’s Hosting Group account. | | R2 | Shared original and public media storage owned by the Organization. | | Cloudflare DNS and URL rules | Domain setup and media aliases where the connected account has the required permissions. | The shared builder uses a Worker project for build execution. A dynamic Worker does not serve each customer page. This publishing setup does not require a D1 database or KV namespace per website. The CMS, Forms and Extension runtime run separately from the static website. A form submission or an Extension can still call its runtime owner. See [Self-hosting](https://typeroll.com/docs/guides/self-hosting/) for CMS infrastructure requirements. ## Set up once per Organization [Section titled “Set up once per Organization”](https://typeroll.com/docs/publishing/cloudflare/#set-up-once-per-organization) Open **Account → Publishing**. Follow [Connect your accounts](https://typeroll.com/docs/guides/customer-publishing/) to connect GitHub, the Default Cloudflare account, R2 and the organization domains. Then find **Builds** and select **Build provider**. ### Cloudflare builds [Section titled “Cloudflare builds”](https://typeroll.com/docs/publishing/cloudflare/#cloudflare-builds) 1. Select **Cloudflare**. Approve build permissions if requested, then select **Set up shared builds**. 2. If Typeroll requests a build token, select **Open Cloudflare setup**. Confirm the account and Worker project shown in Typeroll. 3. Under the Worker’s **Settings → Builds**, connect the generated builder repository and branch `main`. The setup card shows the build and verification commands to use. Create or select the build API token in Cloudflare and save. 4. Return to Typeroll and select **I’ve finished — check again** if the automatic check has not completed. Wait for **Shared build engine ready**. This one-time Cloudflare step authorizes its build service. The token stays in Cloudflare. Site repositories and Hosting Groups reuse the shared build engine; you do not connect each site’s repository to Pages’ built-in Git builder. If Cloudflare changes its interface, use the linked [build token documentation](https://developers.cloudflare.com/workers/ci-cd/builds/configuration/#api-token). ### GitHub Actions builds [Section titled “GitHub Actions builds”](https://typeroll.com/docs/publishing/cloudflare/#github-actions-builds) 1. Select **GitHub Actions** in **Build provider**. 2. If needed, follow **Approve GitHub update** in **GitHub account** and return after approving the existing App installation’s permission update. 3. Select **Set up GitHub builds**. Typeroll creates the shared private build repository and runs a verification build. 4. Once ready, select **Use GitHub Actions for new builds**. The choice applies to new publications. Already queued or running jobs retain their selected provider. Build usage belongs to the selected provider’s customer account; check its current allowance and billing separately. ## Hosting Groups [Section titled “Hosting Groups”](https://typeroll.com/docs/publishing/cloudflare/#hosting-groups) Default is created with the Organization and reuses its existing Cloudflare connection. Additional Hosting Groups connect other Cloudflare accounts and site address bases, such as `sites2.example.com`. Shared media and GitHub remain owned by the Organization. With **Use organization DNS connection**, a group’s site address base can be under a domain managed by the Organization’s DNS account while its Pages projects live in the group’s different hosting account. The group does not need its own root domain just because the hosting account differs. ## Media and generated repositories [Section titled “Media and generated repositories”](https://typeroll.com/docs/publishing/cloudflare/#media-and-generated-repositories) Original images are not committed to GitHub. During a build, the engine receives access to the media required for that frozen publication and includes referenced public media in the static output. This also works across Hosting Group accounts. Shared organization media URLs remain available. Optional site media hosts and legacy media paths are handled by [domain settings](https://typeroll.com/docs/publishing/domains/). Once organization storage is verified, new browser uploads go directly to R2. Generated repositories include source, a dependency lock and instructions for independent builds. They are not a complete CMS backup. Manual edits in them are unsupported; make changes through Typeroll’s UI, API or MCP. ## Versions and public availability [Section titled “Versions and public availability”](https://typeroll.com/docs/publishing/cloudflare/#versions-and-public-availability) Select the Site’s **Version** before deployment. `main` is the live version; other versions map to `version-` Git branches and separate test addresses. Non-main versions and default-domain demos use `noindex`. The Publish menu shows queued, building and distribution progress. It exposes the public link after the actual deployment is verified at that address. [Deploy tools](https://typeroll.com/docs/tools/deploy/) provide the same workflow to AI agents. --- # Website and media domains Source: [Website and media domains](https://typeroll.com/docs/publishing/domains/) > Configure organization media, Hosting Group site addresses and site-specific domains, then prepare and verify domain changes before traffic cutover. Different settings own different addresses. You can use only the selected subdomains for Typeroll while keeping the root website, email and unrelated subdomains on other services. | Scope | Setting | Example | | ------------- | ------------------- | ------------------------------------------- | | Organization | Shared media host | `media.example.com` | | Hosting Group | Site address base | `sites.example.com` or `sites2.example.com` | | Site | Website host | `www.customer.example` | | Site | Optional media host | `images.customer.example` | A site address base is a parent for generated site and version addresses. It does not itself serve a website. Default’s base is configured in organization setup; additional groups have their own settings. ## Set organization domains [Section titled “Set organization domains”](https://typeroll.com/docs/publishing/domains/#set-organization-domains) Open **Account → Publishing → Organization domains**. With Cloudflare DNS access, choose a domain and enter the short **Media subdomain** and **Sites subdomain** labels. Review the complete addresses and select **Configure domains**. The refresh icon next to the domain list retrieves newly available zones. For external or agent-managed DNS, expand **Manual settings or external DNS**, supply the full hostnames and follow the returned records. **Check domain status** reports the current result. Media migration and certificate checks can complete after settings have been saved; read each status before publishing. R2’s shared media custom domain must belong to a Cloudflare zone in the same account as its bucket. Keeping authoritative DNS elsewhere requires a supported Cloudflare partial setup; selecting manual DNS does not remove that provider requirement. See [external DNS requirements](https://typeroll.com/docs/guides/customer-publishing/#when-dns-is-hosted-elsewhere). ## Add a Site’s website and media hosts [Section titled “Add a Site’s website and media hosts”](https://typeroll.com/docs/publishing/domains/#add-a-sites-website-and-media-hosts) Open **Site settings → Publishing → Website and media addresses**. Enter the website host and optional media host. Choose the media path prefix: a separate media hostname can use an empty prefix, while sharing the website host can use `/media`. A media item’s `public_path` can retain an old path such as `/wp-content/uploads/2024/photo.jpg`. The next publication uses the selected preferred host and paths. Existing organization media links and previously published aliases remain usable; changing a preferred host does not invalidate them. Referenced media is included in static output, not committed as images to GitHub. ## Prepare before switching traffic [Section titled “Prepare before switching traffic”](https://typeroll.com/docs/publishing/domains/#prepare-before-switching-traffic) 1. Save the future hosts. This records the intended addresses while the current public website remains active. 2. Select **Prepare domain change from published content**. The candidate uses the last successful publication, not newer saved CMS edits. The build updates internal absolute links, canonicals, sitemap URLs and media references. 3. Follow **Domain verification**, applying the returned validation records if DNS is managed externally. Wait for the required artifact and certificate checks. 4. When allowed, select **Switch website traffic**, or apply the reviewed DNS change through your provider or AI agent. 5. Wait for Typeroll to verify the actual public address. Some Pages hostname validations require traffic DNS to point at Cloudflare before a certificate can be issued. Typeroll keeps the change blocked when it cannot verify a safe candidate. The workflow does not guarantee a seamless cutover for every provider configuration. See the [full domain-change procedure](https://typeroll.com/docs/guides/customer-publishing/#prepare-a-domain-change-before-switching-traffic). ## Use your own AI agent [Section titled “Use your own AI agent”](https://typeroll.com/docs/publishing/domains/#use-your-own-ai-agent) The API and MCP return the same domain requirements as the UI. An agent with its own DNS-provider access can apply them, then request verification in Typeroll. Domain writes use a configuration revision to reject stale changes. Organization connections require an organization key; site domain writes require site admin permission. See the [API and MCP mapping](https://typeroll.com/docs/guides/customer-publishing/#api-and-mcp). For sites still using the older managed publishing mode, use the separately labelled [legacy managed-domain guide](https://typeroll.com/docs/guides/custom-domain/). --- # Booking-link attribution Source: [Booking-link attribution](https://typeroll.com/docs/recipes/booking-link-attribution/) > Preserve advertising parameters across visits and forward them to an external booking link without inventing campaign values. This recipe configures [Analytics attribution](https://typeroll.com/docs/apps/funnel-attribution/) for a landing page that sends visitors to an external booking service. It is generic: replace the example paths and destination with values from your own site. ## Intended behavior [Section titled “Intended behavior”](https://typeroll.com/docs/recipes/booking-link-attribution/#intended-behavior) * Forward `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, and `utm_term` from the current landing-page URL. * After optional consent, preserve first-touch and last-touch snapshots for 30 days. * Prefer current URL values; otherwise use stored last-touch values. * Add no fallback values. A visitor with neither current nor stored campaign data reaches the original clean booking URL. ## Example rule [Section titled “Example rule”](https://typeroll.com/docs/recipes/booking-link-attribution/#example-rule) ```json { "id": "campaign-booking", "page_paths": ["/campaign/"], "source": "current_or_stored", "parameters": [ { "from": "utm_source" }, { "from": "utm_medium" }, { "from": "utm_campaign" }, { "from": "utm_content" }, { "from": "utm_term" } ], "targets": [ { "type": "link", "host": "booking.example.com", "path": "/consultation", "click_event": "booking_click", "destination": "booking" } ], "storage": { "enabled": true, "ttl_days": 30, "touch": "both", "read_touch": "last_touch", "consent": "optional" } } ``` 1. **Read the existing app state.** Preserve unrelated funnel rules. 2. **Confirm the consent surface.** Storage will not write until optional consent is accepted. 3. **Save and deploy.** The runtime is included in the static customer build. 4. **Verify current attribution.** Use different values for every UTM field and inspect the rewritten booking link without navigating. 5. **Verify stored attribution.** In a controlled browser, grant consent, visit once with campaign values, then revisit without query parameters. The link should reuse the stored last-touch snapshot. 6. **Verify the empty case.** With a fresh browser and no UTM values, the booking URL must remain free of synthetic campaign parameters. 7. **Verify the event without navigating.** When Analytics is enabled, inspect the non-blocking `booking_click` request and confirm it contains only the allowlisted campaign fields. Do not create a booking merely to test it. --- # Skills and recipes Source: [Skills and recipes](https://typeroll.com/docs/skills/overview/) > Use Typeroll's optional Markdown workflow recipes with your own AI agent for site building, editing, migration and SEO. Skills are Markdown instructions that explain a workflow to your AI agent. They supplement the MCP tool descriptions: a recipe can tell the agent what to read first, which changes to make and what to verify afterward. Typeroll’s recipes are ordinary files. Clients differ in how they discover and load skills. You can install them into a client-supported location, adapt them to its conventions or ask the agent to read a specific file. Using MCP does not require automatic skill loading or a particular AI provider. ## Install the bundled recipes [Section titled “Install the bundled recipes”](https://typeroll.com/docs/skills/overview/#install-the-bundled-recipes) ```bash npx @typeroll/mcp-server install-skills ./typeroll-skills ``` The command copies the bundled files without contacting your portal. It does not need an API key. Existing files are skipped; add `--force` only when you want to replace them. Keep custom instructions in separate files to preserve them when updating the bundled recipes. ## Choose a workflow [Section titled “Choose a workflow”](https://typeroll.com/docs/skills/overview/#choose-a-workflow) | Workflow | Recipe | | ---------------------------- | ----------------------------------------------------------------------------- | | Create a website | [New site](https://typeroll.com/docs/skills/tr-new-site/) | | Set colors and typography | [Branding](https://typeroll.com/docs/skills/tr-brand/) | | Add a blog | [Blog](https://typeroll.com/docs/skills/tr-blog/) | | Set up a form | [Forms](https://typeroll.com/docs/skills/tr-forms/) | | Check metadata and indexing | [SEO](https://typeroll.com/docs/skills/tr-seo/) | | Import non-WordPress content | [Import from URL](https://typeroll.com/docs/skills/tr-import-url/) | | Migrate WordPress | [WordPress migration](https://typeroll.com/docs/skills/tr-migrate-wp/) | | Coordinate several sites | [Multisite migration](https://typeroll.com/docs/skills/tr-migrate-multisite/) | A recipe is guidance, not a permission grant. Your agent still needs an [API or MCP connection](https://typeroll.com/docs/getting-started/mcp-server/), and its key limits which sites and actions it can access. Review changes before saving and deploying. --- # tr-blog — Set Up a Blog Source: [tr-blog — Set Up a Blog](https://typeroll.com/docs/skills/tr-blog/) > Create a blog or news section — collection schema, seed articles, listing page, individual article pages, and deploy. **Triggers on:** “add a blog”, “set up news”, “article section”, “create posts”, “inlägg”, “nyheter” ## What it does [Section titled “What it does”](https://typeroll.com/docs/skills/tr-blog/#what-it-does) 1. Creates a `blog` collection with a schema (title, slug, date, author, excerpt, body, image, tags) 2. Seeds 2–3 real articles with your content 3. Generates the listing page (`/blog`) with a card grid 4. Creates individual article pages (`/blog/article-slug`) 5. Updates the header navigation to include a link to the blog 6. Deploys ## Important: static site means manual sync [Section titled “Important: static site means manual sync”](https://typeroll.com/docs/skills/tr-blog/#important-static-site-means-manual-sync) Typeroll generates a static site — there is no template engine that auto-renders collection items. Instead, the AI agent writes the listing HTML directly from the current items. This means: * **Adding a new article** requires updating the listing page HTML too * the AI agent handles this automatically when you say “add a new article to the blog” * There is no magic sync — ask the AI agent explicitly after adding articles Caution If you add articles directly in the portal’s Collections UI (not through the AI agent), the listing page won’t update automatically. Ask the AI agent to “regenerate the blog listing” afterwards. ## Example prompts [Section titled “Example prompts”](https://typeroll.com/docs/skills/tr-blog/#example-prompts) ```plaintext Add a blog to our site. The first three articles should be: 1. "Vår designfilosofi" — about our minimal approach 2. "Projekt: Östermalm" — a completed interior project 3. "Höstens trender 2025" — trend roundup ``` ```plaintext Add a new blog post: "Working with natural materials" — about our use of wood, stone and linen. Published today. Author: Maria Svensson. ``` ## Field name rules [Section titled “Field name rules”](https://typeroll.com/docs/skills/tr-blog/#field-name-rules) Field names must be lowercase ASCII only. Swedish and other Nordic characters must be transliterated: | Wrong | Right | | ------------ | ----------------------------------------------- | | `rubrik` | `title` (or `rubrik` — ASCII, this one is fine) | | `författare` | `forfattare` | | `datum` | `date` (or `datum` — fine) | | `ämne` | `amne` | The `label` shown in the portal UI can be any text — only the internal `name` must be ASCII. ## Article page structure [Section titled “Article page structure”](https://typeroll.com/docs/skills/tr-blog/#article-page-structure) Each article gets its own page with `kind: "article"` which activates automatic Article JSON-LD schema: ```plaintext /blog/ ← listing page /blog/var-designfilosofi ← individual article /blog/projekt-ostermalm ``` Slugs must be globally unique — the `/blog/` prefix prevents collisions with other pages. --- # tr-brand — Branding and Design Source: [tr-brand — Branding and Design](https://typeroll.com/docs/skills/tr-brand/) > Palette recipes by mood, typography pairings, and CSS variable setup for your Typeroll site. **Triggers on:** “create a brand”, “choose colours”, “design the look”, “make it feel more…” ## What it does [Section titled “What it does”](https://typeroll.com/docs/skills/tr-brand/#what-it-does) The AI agent picks a coherent colour palette and typography pairing based on the brand’s mood, then applies it via `update_site_settings` and shows a preview. ## Mood presets [Section titled “Mood presets”](https://typeroll.com/docs/skills/tr-brand/#mood-presets) ### Nordic / Minimal [Section titled “Nordic / Minimal”](https://typeroll.com/docs/skills/tr-brand/#nordic--minimal) Calm, professional, lots of white space. ```plaintext primary: #1a1a2e (deep navy) secondary: #f8f9fa (off-white) accent: #e8c86e (warm gold) background: #ffffff surface: #f4f4f6 text: #1a1a2e text_light: #6b7280 ``` Fonts: **Inter** (headings) + **Inter** (body) ### Warm / Artisan [Section titled “Warm / Artisan”](https://typeroll.com/docs/skills/tr-brand/#warm--artisan) Earthy, handcrafted, inviting. ```plaintext primary: #2d1b0e (dark espresso) secondary: #f5ede0 (cream) accent: #c4622d (terracotta) background: #faf7f2 surface: #f0e8d8 text: #2d1b0e text_light: #8b6f47 ``` Fonts: **Playfair Display** (headings) + **Lato** (body) ### Modern / Tech [Section titled “Modern / Tech”](https://typeroll.com/docs/skills/tr-brand/#modern--tech) Clean, confident, forward-looking. ```plaintext primary: #0f172a (slate 900) secondary: #f1f5f9 (slate 100) accent: #3b82f6 (blue 500) background: #ffffff surface: #f8fafc text: #0f172a text_light: #64748b ``` Fonts: **Space Grotesk** (headings) + **Inter** (body) ### Editorial / Dark [Section titled “Editorial / Dark”](https://typeroll.com/docs/skills/tr-brand/#editorial--dark) Dramatic, high-contrast, gallery-feel. ```plaintext primary: #f5f0e8 (warm white) secondary: #1a1a1a (near-black) accent: #e8c86e (gold) background: #111111 surface: #1e1e1e text: #f5f0e8 text_light: #9ca3af ``` Fonts: **Cormorant Garamond** (headings) + **Inter** (body) ## Typography pairings [Section titled “Typography pairings”](https://typeroll.com/docs/skills/tr-brand/#typography-pairings) | Heading | Body | Character | | ------------------ | -------------- | -------------------- | | Playfair Display | Lato | Classic editorial | | Fraunces | Inter | Modern + humanist | | Space Grotesk | Inter | Tech/startup | | Cormorant Garamond | Source Serif 4 | Luxury / literary | | DM Serif Display | DM Sans | Refined + accessible | | Clash Display | Satoshi | Bold/contemporary | ## Font size scale [Section titled “Font size scale”](https://typeroll.com/docs/skills/tr-brand/#font-size-scale) ```plaintext size_base: 16 (1rem = 16px — the default) ``` Headings scale from this base using Typeroll’s built-in fluid type scale. ## How the AI agent applies it [Section titled “How the AI agent applies it”](https://typeroll.com/docs/skills/tr-brand/#how-the-ai-agent-applies-it) 1. Asks (or infers from context) which mood fits the brand 2. Calls `update_site_settings` with the full `colors` and `fonts` objects 3. Calls `get_preview_link` so you can see the result 4. Iterates based on your feedback You can ask for adjustments naturally: “make the accent more orange”, “use a heavier serif for headings”, “the background feels too cold”. --- # tr-forms — Add a Form Source: [tr-forms — Add a Form](https://typeroll.com/docs/skills/tr-forms/) > Create contact, booking or signup forms — server-backed, HMAC-protected, with inline JS feedback. **Triggers on:** “contact form”, “add a form”, “let visitors message us”, “booking form”, “formulär” ## What it does [Section titled “What it does”](https://typeroll.com/docs/skills/tr-forms/#what-it-does) Forms in Typeroll are server-backed — submissions go to `/api/forms/submit` and are stored in Firestore. No third-party service needed. The portal’s Submissions inbox shows all received messages. The AI agent: 1. Creates the form definition (`create_form`) 2. Fetches the signed embed token (`read_form`) 3. Generates the embed HTML with the token, honeypot and optional JS feedback 4. Embeds it on the target page (`update_page`) 5. Deploys ## Field types [Section titled “Field types”](https://typeroll.com/docs/skills/tr-forms/#field-types) | Type | Use for | | ---------- | ----------------------------------- | | `text` | Short text, name, company | | `email` | Email addresses (validated) | | `tel` | Phone numbers | | `url` | Website URLs | | `number` | Quantities, ages | | `textarea` | Long messages | | `select` | Dropdown — provide `options: [...]` | | `radio` | Single choice from a list | | `checkbox` | Boolean yes/no | ## Example prompts [Section titled “Example prompts”](https://typeroll.com/docs/skills/tr-forms/#example-prompts) ```plaintext Add a contact form to the Contact page. Fields: name, email, phone (optional), message. Send submissions to hej@acme.se. ``` ```plaintext Add a booking form to the Services page. Fields: name, email, desired date, time slot (09:00/10:00/11:00/14:00/15:00), optional notes. ``` ```plaintext Add a newsletter signup with just an email field at the bottom of the homepage. ``` ## Field name rules [Section titled “Field name rules”](https://typeroll.com/docs/skills/tr-forms/#field-name-rules) Field names must be lowercase ASCII — no Swedish characters: | Wrong | Right | | ------------ | ----------------------------------------------------------- | | `förnamn` | `fornamn` | | `företag` | `foretag` | | `meddelände` | `meddelande` (this one is actually fine — no special chars) | | `ämne` | `amne` | The `label` (what the user sees) can be any text. ## Token expiry [Section titled “Token expiry”](https://typeroll.com/docs/skills/tr-forms/#token-expiry) Caution The `submit_token` in the embedded form HTML is short-lived (24 hours by default). For forms on long-cached static pages, fetch a fresh token and redeploy periodically. On the hosted plan, token refresh is handled automatically. ## Viewing submissions [Section titled “Viewing submissions”](https://typeroll.com/docs/skills/tr-forms/#viewing-submissions) Submissions appear in the portal at `/app/sites/{siteId}/forms/{formId}/submissions`. The portal stores all submissions regardless of the `recipient_email` setting. Email delivery to `recipient_email` depends on email configuration in your portal deployment. On the hosted plan (app.typeroll.com) email delivery is included. ## Protection layers [Section titled “Protection layers”](https://typeroll.com/docs/skills/tr-forms/#protection-layers) Every form submission goes through three protection layers: 1. **Rate limiting** — 30 submissions per 5 minutes per IP 2. **HMAC token** — proves the form was generated by the platform 3. **Honeypot** — hidden `_hp` field that bots fill but humans don’t; filled = rejected --- # tr-import-url — Import from a URL Source: [tr-import-url — Import from a URL](https://typeroll.com/docs/skills/tr-import-url/) > Import content from Squarespace, Wix, Webflow, static HTML or any URL into your Typeroll site. **Triggers on:** “import from Squarespace/Wix/Webflow”, “copy content from”, “rebuild this site”, any non-WordPress URL the user points at For WordPress sources, use [tr-migrate-wp](https://typeroll.com/docs/skills/tr-migrate-wp/) instead. ## What it does [Section titled “What it does”](https://typeroll.com/docs/skills/tr-import-url/#what-it-does) 1. Fetches the source site’s homepage and sitemap to build a URL inventory 2. Learns the target Typeroll site’s design (settings, partials, example pages) 3. Fetches each source page, strips platform-specific classes and structure, transfers images 4. Creates pages as **drafts** in the target site 5. Creates redirects from old URLs to new slugs 6. Shows a preview for review 7. Publishes and deploys after approval ## Platform notes [Section titled “Platform notes”](https://typeroll.com/docs/skills/tr-import-url/#platform-notes) ### Squarespace [Section titled “Squarespace”](https://typeroll.com/docs/skills/tr-import-url/#squarespace) * Content is inside `.content-wrapper` or `[data-section-theme]` blocks * Images are usually high-res originals — they transfer well * Strip all Squarespace JSON-LD (it’s their own schema) * Gallery blocks → the AI agent converts to CSS grid with `` tags ### Wix [Section titled “Wix”](https://typeroll.com/docs/skills/tr-import-url/#wix) * Check what each public page returns; JavaScript and access restrictions can limit direct fetching * the AI agent will ask you for an HTML export or take screenshots as reference * Screenshots are layout references, not a substitute for text and original media ### Webflow [Section titled “Webflow”](https://typeroll.com/docs/skills/tr-import-url/#webflow) * Usually fetchable with clean HTML output * Classes like `w-container`, `w-row`, `w-col-*` are stripped * Webflow CMS items are server-rendered and appear in the HTML ### Static HTML [Section titled “Static HTML”](https://typeroll.com/docs/skills/tr-import-url/#static-html) * Often the cleanest import — fetch, strip nav/footer, keep body * Table-based layouts (pre-2010 sites) get converted to CSS grid See the separate guides for [Squarespace](https://typeroll.com/docs/guides/migrate-from-squarespace/), [Wix](https://typeroll.com/docs/guides/migrate-from-wix/) and [Webflow](https://typeroll.com/docs/guides/migrate-from-webflow/) for migration scope, exclusions and review steps. ## Example prompts [Section titled “Example prompts”](https://typeroll.com/docs/skills/tr-import-url/#example-prompts) ```plaintext Import the content from https://acme.squarespace.com — keep the same pages but adapt them to our current Typeroll design. ``` ```plaintext Rebuild this Wix site in Typeroll: https://acme.wixsite.com/studio Here are the screenshots I exported. ``` ## What gets stripped [Section titled “What gets stripped”](https://typeroll.com/docs/skills/tr-import-url/#what-gets-stripped) * Platform-specific CSS classes (`sqsrte-*`, `wf-*`, `elementor-*`, `fl-*`, `divi-*`, `vc_*`) * ` ``` ## Custom partials [Section titled “Custom partials”](https://typeroll.com/docs/tools/partials/#custom-partials) You can create any number of custom partials. The AI agent can embed them in page HTML or use them as standalone components: ```plaintext Create a "cookie-banner" partial with a GDPR notice. ``` Custom partial IDs can be any lowercase string. Reference them in page HTML as static content — there is no template include syntax in HTML-mode pages; the AI agent copies the partial’s HTML where needed. --- # Redirects Tools Source: [Redirects Tools](https://typeroll.com/docs/tools/redirects/) > Manage URL redirects — essential after migrations and slug changes. Redirects are built into the static site as a Cloudflare Pages `_redirects` file. They’re 301 permanent redirects by default. ## `create_redirect` [Section titled “create\_redirect”](https://typeroll.com/docs/tools/redirects/#create_redirect) Creates a redirect from one path to another. ```plaintext create_redirect from_path="/about" to_path="/om-oss" create_redirect from_path="/services.html" to_path="/tjanster" ``` The AI agent uses redirects automatically after: * WordPress or URL migrations (old paths → new slugs) * Page slug changes (the old path needs to redirect to the new one) ## Wildcards — retire a whole family of URLs [Section titled “Wildcards — retire a whole family of URLs”](https://typeroll.com/docs/tools/redirects/#wildcards--retire-a-whole-family-of-urls) A migrated site’s dead URLs come in shapes, not as individuals. One rule can cover all of them: ```plaintext create_redirect from_path="/category/*" to_path="/blogg/:splat" ``` * A trailing `*` matches everything under the prefix (and the prefix itself); `:splat` inserts what was captured. * `:name` matches exactly one segment: `from_path="/blog/:slug"` → `to_path="/artiklar/:slug"`. * Narrower rules always win, so `/blogg/recept/*` can sit alongside `/blogg/*`. Typical after a WordPress move: | Old shape | Rule | | ---------------------- | ------------------------------- | | `/category/…` archives | `/category/*` → `/blogg/:splat` | | `/tag/…` archives | `/tag/*` → `/blogg` | | `/author/…` archives | `/author/*` → `/om-oss` | | Date permalinks | `/2019/*` → `/blogg/:splat` | Three things Typeroll will refuse, and why: * **A `*` anywhere but the end.** Cloudflare ignores it, so the rule would look saved and do nothing. * **A rule that would hide a live page.** Redirects are applied before files are served, so `/blogg/*` would make every real article under `/blogg/` unreachable. Narrow the prefix instead. * **Query strings.** Redirects match the path only — an old `/?p=123` URL has nothing to key on. ## `list_redirects` [Section titled “list\_redirects”](https://typeroll.com/docs/tools/redirects/#list_redirects) Returns all configured redirects. ## `delete_redirect` [Section titled “delete\_redirect”](https://typeroll.com/docs/tools/redirects/#delete_redirect) Deletes a redirect by its from-path. ## After a slug change [Section titled “After a slug change”](https://typeroll.com/docs/tools/redirects/#after-a-slug-change) If you rename a page’s slug, always create a redirect: ```plaintext Rename the "services" page to "what-we-do" and set up a redirect from /services. ``` The AI agent handles both steps automatically when you phrase it this way. ## Redirect limits [Section titled “Redirect limits”](https://typeroll.com/docs/tools/redirects/#redirect-limits) Cloudflare Pages supports up to 2,100 redirects per site. For large migrations, the AI agent prioritises the most-trafficked URLs first. --- # Settings Tools Source: [Settings Tools](https://typeroll.com/docs/tools/settings/) > Site-wide configuration — colours, fonts, contact info, social links, SEO defaults. ## `read_site_settings` [Section titled “read\_site\_settings”](https://typeroll.com/docs/tools/settings/#read_site_settings) Returns all current site settings. ## `update_site_settings` [Section titled “update\_site\_settings”](https://typeroll.com/docs/tools/settings/#update_site_settings) Updates site settings. Pass only the fields you want to change. ### Top-level fields [Section titled “Top-level fields”](https://typeroll.com/docs/tools/settings/#top-level-fields) | Field | Type | Description | | -------------------------- | --------- | -------------------------------------------------------- | | `site_name` | string | Display name of the site | | `tagline` | string | Short description, used in SEO and the footer | | `language` | string | BCP 47 language tag: `"sv"`, `"en"`, `"de"`, etc. | | `logo` | string | CDN URL for the site logo | | `favicon` | string | CDN URL for the favicon | | `apple_touch_icon` | string | 180px Apple touch icon | | `icon_192` | string | 192px application icon | | `default_seo_suffix` | string | Appended to page titles in ``: `" — Acme Studio"` | | `default_meta_description` | string | Site-wide description fallback | | `trailing_slash` | string | `always`, `never`, or `ignore` | | `iframe_allowed_hosts` | string\[] | Exact hosts allowed in embedded content | | `image_sizes_default` | string | Default responsive-image `sizes` hint | | `robots_txt` | string | Full content of robots.txt | | `sitewide_noindex` | boolean | Emit `noindex,nofollow` on every HTML page | | `scripts_head` | string | Trusted markup/scripts inserted in `<head>` | | `scripts_body_end` | string | Trusted markup/scripts inserted before `</body>` | | `custom_css` | string | Site-wide CSS | ### `colors` object [Section titled “colors object”](https://typeroll.com/docs/tools/settings/#colors-object) | Field | Default | Description | | ------------ | --------- | --------------------- | | `primary` | `#1a1a2e` | Main brand colour | | `secondary` | `#f8f9fa` | Supporting colour | | `accent` | `#e8c86e` | Pop colour for CTAs | | `background` | `#ffffff` | Page background | | `surface` | `#f4f4f6` | Card/panel background | | `text` | `#1a1a2e` | Main body text | | `text_light` | `#6b7280` | Muted text | ### `fonts` object [Section titled “fonts object”](https://typeroll.com/docs/tools/settings/#fonts-object) | Field | Default | Description | | ----------- | --------- | --------------------------------------- | | `heading` | `"Inter"` | Heading font family (Google Fonts name) | | `body` | `"Inter"` | Body font family | | `size_base` | `16` | Base font size in px | ### `contact` object [Section titled “contact object”](https://typeroll.com/docs/tools/settings/#contact-object) | Field | Description | | --------- | --------------------- | | `email` | Contact email address | | `phone` | Phone number | | `address` | Postal address | ### `social` object [Section titled “social object”](https://typeroll.com/docs/tools/settings/#social-object) | Field | Description | | ----------- | ----------------------- | | `instagram` | Instagram URL or handle | | `facebook` | Facebook URL | | `linkedin` | LinkedIn URL | | `twitter` | X/Twitter URL or handle | | `youtube` | YouTube channel URL | ### `cookie_consent` object [Section titled “cookie\_consent object”](https://typeroll.com/docs/tools/settings/#cookie_consent-object) The native consent banner is configured through the same bearer-authenticated settings route and MCP tool: ```json { "cookie_consent": { "enabled": true, "text": "We use optional cookies.", "privacy_policy_url": "/privacy/", "scripts_necessary": "", "scripts_optional": "<script>startAnalytics()</script>", "reload_after_consent": false } } ``` The object is shallow-merged, so omitted fields keep their saved values. In a signed hosted preview the banner and optional-script gate work, but the frame has an intentionally opaque origin: the choice is held in memory for the current preview document and resets on reload/navigation. A published build uses the normal `tr_consent` cookie. ## Trusted scriptable fields [Section titled “Trusted scriptable fields”](https://typeroll.com/docs/tools/settings/#trusted-scriptable-fields) `scripts_head`, `scripts_body_end`, `custom_css`, and the consent script fields are readable and writable through v1/MCP for a caller holding the site’s API key. They are deliberately trusted, audit-logged surfaces. The chat assistant inside the portal does not expose them, so a normal editor conversation cannot inject JavaScript. Review these values like deployed code and redeploy after a change. ## `get_site` / `list_sites` [Section titled “get\_site / list\_sites”](https://typeroll.com/docs/tools/settings/#get_site--list_sites) `get_site` returns site metadata (ID, name, domain, creation date) plus a `urls` object. `list_sites` returns all sites in your account. The AI agent uses these to confirm which site it’s working on before making changes. ### Is my site live? [Section titled “Is my site live?”](https://typeroll.com/docs/tools/settings/#is-my-site-live) Read `urls.production` from `get_site`. Non-null means a custom domain is verified and serving; null means the site is still on its Typeroll subdomain. There is no site-level “status” field — one existed until 0.30.0, but it was set when the site was created and never updated afterwards, so it reported live sites as “planning” forever. It was removed rather than left to mislead. For “has anything shipped”, use `list_deploys`. ## `create_site` [Section titled “create\_site”](https://typeroll.com/docs/tools/settings/#create_site) Bootstraps a whole new site — takes a name and an optional domain, and provisions the hosting project and fallback subdomain. ```plaintext Create a new site called "Lakeside Cafe" for lakesidecafe.se. ``` Requires an **org-scoped** API key. A site-scoped key can only reach the one site it was issued for, which is the point of the distinction — see [Install the MCP Server](https://typeroll.com/docs/getting-started/mcp-server/). ## `update_site` [Section titled “update\_site”](https://typeroll.com/docs/tools/settings/#update_site) Changes a site’s name, slug or domain. The slug is uniqueness-checked because it determines the fallback subdomain. Resubmitting the current slug is idempotent and repairs missing fallback hosting coordinates, including the Pages project and DNS record, when the hosting provider is configured. ## Exporting your content [Section titled “Exporting your content”](https://typeroll.com/docs/tools/settings/#exporting-your-content) `export_site` (and **Settings → Export** in the portal) downloads your entire site as JSON — pages, blocks, partials, collections and their items, settings, redirects, forms and media metadata. It’s a plain, documented shape rather than a proprietary blob: your content is yours, and this is the door out. It’s also the fastest way to hand a site to another environment, or to snapshot before a large restructuring.