Owner answers and review
This guide describes the owner-review contract in the current Core 0.2.77 release. Core 0.2.24 does not include it. Updating an MCP client alone does not update the connected CMS. It applies to Typeroll Cloud and self-hosted Typeroll CMS. An installed Extension may verify a visitor and submit proposals through its scoped server credential; its authentication and setup instructions belong to that installation’s guide.
Configure review
Section titled “Configure review”A Site administrator opens Site settings → Integrations → Review owner changes, enters one
reviewer email, chooses a link lifetime (1–168 hours), and enables proposals.
Configure the site’s Email & notifications connector and sender separately.
Self-hosted servers also need INTEGRATIONS_SECRET_KEY for encrypted review links.
No email is sent simply by saving these settings.
The queue shows pending proposals and notification state. Opening a private review link requires no login. It grants access to that proposal only. Treat it as a secret: do not paste it into public issues, analytics or shared documents. Opening or scanning the link never approves it. The reviewer chooses Approve changes, Reject changes, or edits the proposed answers before approving. Original owner answers and reviewer adjustments remain separate in the audit record. A changed Page or schema blocks stale approval; ask for a fresh proposal against the current content instead of forcing an overwrite.
Pending proposals live outside Pages, working copies and publication snapshots. An unrelated publication cannot include them. Approval changes accepted CMS content but does not start a deployment or enable automatic publishing. An existing scheduled publication can include already accepted changes; otherwise use the normal publication action after reviewing the accepted content.
Typed answers and sources
Section titled “Typed answers and sources”A boolean answer has three states: true, false, and unknown (null or absent).
The custom-field editor and form/boolean offer Yes, No, and Clear
answer. Omit untouched answers from a patch. An intentional clear sends null;
it does not send false. Required answers cannot be cleared when saving complete
content. Text, numbers, selections, references and structured fields keep their
own controls. A missing multiselect option says nothing about a binary answer.
For individually protected options use an object of boolean fields, not a multiselect whose missing choices would be interpreted as No:
{ "name": "support", "label": "Support offered", "type": "object", "writable_by": ["portal", "owner", "agent", "import"], "fields": [ { "name": "online", "label": "Online support?", "type": "boolean" }, { "name": "phone", "label": "Phone support?", "type": "boolean" } ]}Child permissions inherit their parent’s policy unless explicitly declared.
Structured arrays need item_key, naming a text child with a unique stable ID.
Reordering preserves identity; removing a protected item or clearing its parent
still checks every protected answer. Unkeyed arrays are one indivisible value.
Do not invent IDs or sources while silently migrating existing data: prepare and
review an explicit mapping first.
The CMS assigns actor identity and time. Provenance follows schema paths such as
support/online and programs/@program-id/tax_id_required; path segments escape
~ as ~0 and / as ~1. Owner-facing forms receive source kind and time, not
private actor identities. Saving an unchanged imported value does not confirm it
as an owner answer. Explicit confirmation of unchanged answers is not offered.
update_page and replace_page accept optional answer_sources alongside their
patch/page. Each path accepts only source_url and import_run_id. These provide
evidence, never permission to impersonate another writer:
{ "page_id": "sample", "patch": { "fields": { "support": { "online": true } } }, "answer_sources": { "support/online": { "source_url": "https://example.org/support", "import_run_id": "reviewed-import-01" } }, "save": true}Owner answers, including No and deliberate clears, outrank later imports and
machine passes. Editors, in the portal or through the API and MCP, retain higher
priority. An administrator
replacing an owner answer must provide an explicit override reason through the
review administration API, or send override_reason on a Page write through the
API or MCP. Conflicts return 409; do not retry the same overwrite.
Separate nonconflicting updates when an entire submitted field is rejected.
rendered: false controls public output independently of write authority.
API and MCP
Section titled “API and MCP”All administration below requires Site administrator access. Ordinary Extension
credentials cannot use these endpoints. version selects an existing content
version; unknown versions are rejected.
GET /api/v1/sites/:site/owner-review: settings and paginated queue (cursor).POSTto the same endpoint acceptsaction:settings,view,approve,reject,notify,recover-notification,revoke,review-link, oroverride.- Proposal actions identify
proposal_id; decisions also need a stablerequest_id(16–100 letters, numbers,_or-). An approval may includeadjustments. Replaying the same decision is safe; a competing decision fails. overrideneedspage_id,base_revision, customfieldsand a nonemptyreason. It uses the same field protections and records an explicit audit.- Scoped owner Extensions read
GET .../pages/:page/owner-fieldsfor allowed fields andrevision.PUTsubmits{changes,base_revision,request_id}and a server-derivedX-Typeroll-Owner-Subjectdigest. Never expose the installation credential to a browser. Success is 202/pending, not a Page update.
MCP provides list_owner_proposals, read_owner_proposal,
configure_owner_review, decide_owner_proposal,
retry_owner_review_notification, recover_owner_review_notification and
revoke_owner_proposal. A notification retry sends transactional email and
requires the appropriate authorization.
Independently saved review groups
Section titled “Independently saved review groups”From Core 0.2.99, an owner Extension can submit a multi-step editor without a final submission gate. Each saved step immediately enters normal moderation. Publishing still reads only accepted Page values; no pending answers become public.
An optional group_id (1–80 letters, numbers, _ or -, excluding reserved object
keys) identifies one set of answers for this owner, installation, Page and version.
Send group_revision: null for its first save, or its latest revision from the
owner descriptor. Keep request_id stable when retrying an uncertain save.
changes contains only deliberate edits; it is not a complete profile snapshot.
Authenticated GET /pages/:page/owner-fields with the server-derived
X-Typeroll-Owner-Subject returns review_groups: each group has revision,
status and changes. Only current pending answers are included in changes.
Accepted, rejected, revoked or conflicting groups do not replace current Page data.
Other owners’ groups are never returned.
Updating a pending group atomically replaces its previous proposal and revokes its old review link. Returning every answer to its accepted value cancels the pending change. Separate groups can be approved independently: approval checks the schema and the original values of the affected fields, plus atomic storage guards. A concurrent change to the same field blocks approval. A reviewer editing unrelated fields in an old proposal must refresh the full profile first.
Apps should restore saved answers on return, preserve exact uncertain requests, and explain when a same-field conflict needs an editor. An abandoned form must not prevent review of earlier saved groups. No invitation, site publication or data migration is triggered by enabling this contract.
Delivery failure and recovery
Section titled “Delivery failure and recovery”A proposal is saved before notification delivery. A failed email leaves it in the
queue. There are at most three delivery attempts, including explicit retries.
An interrupted sending attempt is never automatically timed out and resent:
an administrator first investigates the provider result and records a recovery
reason. Recovery does not reset the attempt count. The queue remains accessible
through normal authenticated administration if the email link expires.
Private review pages use no-store, noindex, no-referrer and no third-party assets. The token is carried in a URL fragment, removed from history on load, and sent in a bearer header. Reloading the cleaned URL requires reopening the original link. The document store performs decision, Page and history writes in one transaction. The filesystem fixture datastore is for local testing and does not provide multi-document crash recovery; use a deployed installation for live review.
Migration checklist
Section titled “Migration checklist”Review current writable_by rules and identify stable array IDs before enabling
owner proposals. Preserve evidence and unknown values. Do not convert legacy
unchecked options or ambiguous false values into confirmed No. Existing working
copies based on older accepted answers must be refreshed before saving. Update
participating Extensions and republish affected form/component pages after the
matching release. Publishing Core does not activate a site’s owner workflow or
send invitations automatically.
Before issuing a visitor edit link, an app must read the owner-fields descriptor
and require review_ready: true. False means the site review settings or host
secret encryption setup is incomplete; do not send a link into an unusable flow.
Delivery can still fail later; pending proposals remain in the administrator queue.
Administrators can use read_owner_answers to inspect typed values and the current
revision, then override_owner_answers for an explicitly authorized correction
with a factual reason. An import conflict never authorizes such an override.
recover_owner_review_notification records an investigated interrupted delivery;
it does not send email or reset the attempt limit. The queue exposes the same
recovery action. Use retry_owner_review_notification only after authorization
to send the transactional notification.