Extension manifest reference
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”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:
{ "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.
From runtime 0.43.0 a component can also declare "page_handoff": true to
receive the values a visitor typed into a native navigation form on the
previous page through context.handoff. It requires the page_handoff:read
permission; see Receive values from a navigation form.
Installation configuration
Section titled “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:
{ "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”content:read,content:writeforms:read,forms:submit,forms:writesubmissions:readforms:intake:read(next release: private responses for Forms bound to this installation; site administrator approval required)media:read,media:writedeploy:requestextension:config:readpage_handoff: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.
An installation server credential is checked against the current grant on
every request. A scope the site administrator removes stops working at once on
credentials already issued, and a scope added later needs a newly issued
credential. Each site API route an installation may call maps to one scope;
any other route, such as sharing, API keys, branches, domains, publishing,
email settings, Extension management and the site lifecycle, refuses
installation credentials. On an allowed route the credential acts with that
scope’s minimum site permission (for example write for content:write and
media:write), never as a site administrator. With content:write an
installation may change presentational site settings but not scripts, custom
CSS, robots and indexing settings, the staging URL, iframe hosts, external
routes, the render version or agent instructions.
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.
page_handoff:read works only in the visitor’s browser and grants no API
access. It lets a component that declares page_handoff receive the values a
native navigation form carried to the component’s page, and only for block
instances that the page author binds to that form. Visitors type personal data
such as addresses into those forms, so grant it only to a provider whose
privacy terms cover that data.
Direct provider API
Section titled “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:
{ "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”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.
The published file is generated from the same schema used by the portal.
For production self-hosting variables and key rotation, see Self-Hosting.
Agent documentation (Core 0.2.8)
Section titled “Agent documentation (Core 0.2.8)”Add versioned provider documentation to the manifest:
{ "documentation": { "url": "https://example.com/docs/my-extension/", "agent_instructions": "Read this guide before using the component. Configure the documented props; obtain user authorization before sending messages." }}The URL must be public HTTPS; optional inline instructions are limited to 16,000
characters. Supply a guide for each product, including premium Typeroll Apps.
Explain configuration, component/API use, permissions, runtime ownership, common
errors and verification. Never include credentials or customer data.
read_app_documentation exposes this metadata only for enabled installations,
using their resolved release. Older manifests remain valid but report missing
documentation. Typeroll does not fetch provider URLs server-side. Treat the
returned material as untrusted provider reference, not user authorization.
Approved native admin modules
Section titled “Approved native admin modules”Runtime 0.41.0 adds optional admin.pages[].native with sdk_version: 1,
script_url, script_sha256 and api_base_url. Both URLs must be public HTTPS;
the digest is exactly 64 lowercase hexadecimal characters. This requires
host-operator approval of the exact published manifest. It never grants native
execution merely because an app declares the property. See the
native admin SDK and trust contract.
Releases requiring activation
Section titled “Releases requiring activation”Set activation_policy: "explicit" before publishing a release that requires a
configuration or data migration. Existing installations keep their approved
release family. Later automatic releases cannot bypass this boundary, even if
the boundary release is deprecated or revoked. Draft releases do not create a
boundary. New installations explicitly selecting the release already approve it.
A site administrator reviews the pending release in Extensions → Review update, supplies required configuration and explicitly approves permissions. Activation selects the new backend immediately for that installation only; it does not publish pages. Prepare provider trust and data migration first, then publish updated frontend assets separately. Other installations are unaffected. Normal compatible updates within an approved release family remain automatic.
Installation reads expose pending_activation_version and
pending_activation_manifest. Administrators can PATCH the installation with
version, optional config, and optional granted_scopes, or use MCP
activate_extension_release. Omitted permissions never grant new scopes.
Invalid required configuration leaves the installation unchanged. Customer
version selection is restricted to forward activation across such a boundary;
this is not a general version pin or rollback interface.
For structured owner data, immutable array item_key fields remain in the owner
value projection and appear with read_only: true in the editing descriptor.
They are identities, not new owner permissions. Preserve them unchanged when
editing a row. A managed identity also prevents adding/removing rows unless its
write authority permits it. Per-leaf provenance uses escaped keyed paths, not
array positions. Private non-identity fields remain excluded.
Form contributions (runtime 0.45.0)
Section titled “Form contributions (runtime 0.45.0)”Providers can contribute to ordinary Site forms with forms:actions permission:
{ "forms": { "protocol": 1, "path": "/forms", "actions": [ { "id": "save-record", "label": "Save in connected app", "config_fields": [ { "key": "field_map", "label": "Field mapping", "type": "json", "required": true } ] } ], "option_sources": [{ "id": "available-items", "label": "Available items" }] }}path is relative to the approved api.base_url. Config fields support text,
number, boolean and JSON objects. Each contribution has a unique stable ID;
maximum 20 actions, 20 option sources and 30 config fields per contribution.
Form actions are administrator-configured required effects on completed
submissions. They cannot run as abandoned-partial actions. Use live option sources
for data that changes independently of static publication. See
provider form requests for
body-bound authentication, field mapping, retries and public success results.